Testar código em geral não é fácil. Se assim fosse, estaríamos fazendo tudo isso há muito tempo, e não lidando com isso apenas nos últimos 10 a 15 anos. Uma das maiores dificuldades sempre foi determinar como testar o código que foi escrito de forma coesa, bem fatorada e testável sem quebrar o encapsulamento. O diretor do BDD sugere que nos concentremos quase inteiramente no comportamento e, de certa forma, parece sugerir que você realmente não precisa se preocupar com os detalhes internos em um grau tão alto, mas isso pode dificultar bastante as coisas de testar se houver muitos métodos particulares que fazem "coisas" de uma maneira muito oculta, pois podem aumentar a complexidade geral do seu teste para lidar com todos os resultados possíveis em um nível mais público.
A zombaria pode ajudar até certo ponto, mas, novamente, é bastante focada externamente. A injeção de dependência também pode funcionar muito bem, novamente com simulações ou testes duplos, mas isso também pode exigir que você exponha elementos por meio de uma interface ou diretamente, que você preferiria permanecer oculto - isso é particularmente verdadeiro se você desejar tenha um bom nível de segurança paranóica sobre certas classes dentro do seu sistema.
Para mim, o júri ainda não decidiu se deve projetar suas aulas para serem mais facilmente testáveis. Isso pode criar problemas se você precisar fornecer novos testes enquanto mantém o código legado. Aceito que você possa testar absolutamente tudo em um sistema, mas não gosto da ideia de expor - mesmo que indiretamente - os internos particulares de uma classe, apenas para que eu possa escrever um teste para eles.
Para mim, a solução sempre foi adotar uma abordagem bastante pragmática e combinar várias técnicas para atender a cada situação específica. Eu uso muitas duplas de teste herdadas para expor propriedades e comportamentos internos para meus testes. Eu zombo de tudo o que pode ser anexado às minhas classes e, quando isso não comprometer a segurança de minhas classes, fornecerei um meio de substituir ou injetar comportamentos para fins de teste. Considerarei até fornecer uma interface mais orientada a eventos se isso ajudar a melhorar a capacidade de testar o código
Onde encontro qualquer código "não testável" , procuro ver se consigo refatorar para tornar as coisas mais testáveis. Onde você tem muito código privado ocultando os bastidores, geralmente encontrará novas classes esperando para serem desdobradas. Essas classes podem ser usadas internamente, mas geralmente podem ser testadas de forma independente, com menos comportamentos privados e, geralmente, com menos camadas de acesso e complexidade. Uma coisa que eu evito, no entanto, é escrever código de produção com código de teste embutido. Pode ser tentador criar " terminais de teste " que resultem na inclusão de horrores como if testing then ...
, o que indica um problema de teste não totalmente desconstruído e incompletamente resolvido.
Talvez seja útil ler o livro xUnit Test Patterns de Gerard Meszaros , que cobre todo esse tipo de coisa com muito mais detalhes do que eu posso entrar aqui. Provavelmente não faço tudo o que ele sugere, mas ajuda a esclarecer algumas das situações mais difíceis de lidar. No final do dia, você deseja satisfazer seus requisitos de teste enquanto ainda aplica seus projetos preferidos, e isso ajuda a entender melhor todas as opções para decidir melhor onde você pode se comprometer.