Só agora estou aprendendo TDD. Entendo que os métodos privados não são testáveis e não devem se preocupar, porque a API pública fornecerá informações suficientes para verificar a integridade de um objeto.
Eu entendi OOP por um tempo. Entendo que métodos privados tornam os objetos mais encapsulados, portanto, mais resistentes a alterações e erros. Portanto, eles devem ser usados por padrão e apenas os métodos que importam para os clientes devem ser tornados públicos.
Bem, é possível criar um objeto que possua apenas métodos particulares e interaja com outros objetos ouvindo seus eventos. Isso seria muito encapsulado, mas completamente não testável.
Além disso, é considerado uma prática ruim adicionar métodos para testar.
Isso significa que o TDD está em desacordo com o encapsulamento? Qual é o saldo apropriado? Estou inclinado a tornar a maioria ou todos os meus métodos públicos agora ...
fonte
Respostas:
Prefira testar à interface do que testar na implementação.
Isso depende do seu ambiente de desenvolvimento, veja abaixo.
É isso mesmo, o TDD se concentra em testar a interface.
Métodos privados são um detalhe de implementação que pode mudar durante qualquer ciclo de re-fatoração. Deve ser possível re-fatorar sem alterar a interface ou o comportamento da caixa preta . De fato, isso faz parte do benefício do TDD, a facilidade com a qual você pode gerar a confiança de que mudanças internas a uma classe não afetarão os usuários dessa classe.
Mesmo se a classe não tem métodos públicos, é manipuladores de eventos são é interface pública , e seu contra essa interface pública que você pode testar.
Como os eventos são a interface, são os eventos que você precisará gerar para testar esse objeto.
Procure usar objetos simulados como a cola para o seu sistema de teste. Deve ser possível criar um objeto simulado simples que gere um evento e capte a mudança de estado resultante (possível por outro objeto simulado do receptor).
Absolutamente, você deve ter muito cuidado com a exposição do estado interno.
Absolutamente não.
O TDD não deve alterar a implementação de suas classes além de talvez simplificá-las (aplicando o YAGNI de um ponto anterior).
As melhores práticas com TDD são idênticas às melhores práticas sem TDD, basta descobrir o motivo mais cedo, porque você está usando a interface enquanto a desenvolve.
Isso seria jogar o bebê fora com a água do banho.
Você não precisa tornar todos os métodos públicos, para poder desenvolver de maneira TDD. Veja minhas anotações abaixo para ver se seus métodos privados são realmente intratáveis.
Uma visão mais detalhada do teste de métodos privados
Se você absolutamente deve testar alguns comportamentos particulares de uma classe, dependendo do idioma / ambiente, você pode ter três opções:
Obviamente, a terceira opção é de longe a melhor.
1) Coloque os testes na classe que você deseja testar (não é o ideal)
Armazenar casos de teste no mesmo arquivo de classe / origem que o código de produção em teste é a opção mais simples. Mas sem muitas diretivas ou anotações pré-processador, você terminará com o código de teste inchando seu código de produção desnecessariamente e, dependendo de como você estruturou seu código, poderá acabar expondo acidentalmente a implementação interna para os usuários desse código.
2) Exponha os métodos particulares que você deseja testar como métodos públicos (realmente não é uma boa ideia)
Conforme sugerido, essa é uma prática muito ruim, destrói o encapsulamento e expõe a implementação interna aos usuários do código.
3) Use um ambiente de teste melhor (melhor opção, se estiver disponível)
No mundo Eclipse, 3. pode ser alcançado usando fragmentos . No mundo C #, podemos usar classes parciais . Outros idiomas / ambientes geralmente têm funcionalidades semelhantes, basta encontrá-las.
Assumindo cegamente que 1. ou 2. são as únicas opções, provavelmente resultaria em um software de produção inchado com código de teste ou interfaces de classe desagradáveis que lavam a roupa suja em público. * 8 ')
fonte
Claro que você pode ter métodos particulares e, claro, pode testá-los.
Existe alguma maneira de executar o método privado; nesse caso, você pode testá-lo dessa maneira ou não há como executar o privado, nesse caso: por que diabos você está tentando testá-lo, apenas apague a maldita coisa!
No seu exemplo:
Por que isso seria testável? Se o método for chamado em reação a um evento, basta fazer com que o teste alimente o objeto com um evento apropriado.
Não se trata de não ter métodos particulares, é de não quebrar o encapsulamento. Você pode ter métodos privados, mas deve testá-los através da API pública. Se a API pública for baseada em eventos, use eventos.
Para o caso mais comum de métodos auxiliares privados, eles podem ser testados através dos métodos públicos que os chamam. Em particular, como você só pode escrever código para obter aprovação em um teste com falha e seus testes estão testando a API pública, todo o novo código que você escreve geralmente será público. Os métodos privados aparecem apenas como resultado de uma refatoração de método de extração , quando são retirados de um método público já existente. Mas, nesse caso, o teste original que testa o método público também cobre o método privado, já que o método público chama o método privado.
Portanto, geralmente os métodos privados só aparecem quando são extraídos de métodos públicos já testados e, portanto, também já são testados.
fonte
internal
métodos ou métodos públicos nasinternal
classes devem ser testados diretamente com bastante frequência. Felizmente, o .net suporta o testeInternalsVisibleToAttribute
, mas sem ele, seria um PITA.Ao criar uma nova classe no seu código, você o faz para responder a alguns requisitos. Os requisitos dizem o que o código deve fazer, não como . Isso facilita a compreensão de por que a maioria dos testes acontece no nível de métodos públicos.
Por meio de testes, verificamos que o código faz o que é esperado , lança exceções apropriadas quando esperado etc. Nós realmente não nos importamos como o código é implementado pelo desenvolvedor. Embora não nos importemos com a implementação, ou seja, como o código faz o que faz, faz sentido evitar testar métodos privados.
Quanto ao teste de classes que não possuem métodos públicos e interagem com o mundo externo apenas por meio de eventos, você também pode testar isso enviando, através de testes, os eventos e ouvindo a resposta. Por exemplo, se uma classe precisar salvar um arquivo de log toda vez que receber um evento, o teste de unidade enviará o evento e verificará se o arquivo de log foi gravado.
Por último, mas não menos importante, em alguns casos, é perfeitamente válido testar métodos privados. É por isso que, por exemplo, no .NET, você pode testar não apenas classes públicas, mas também privadas, mesmo que a solução não seja tão direta quanto aos métodos públicos.
fonte
Não concordo com essa afirmação, ou diria que você não testa métodos particulares diretamente . Um método público pode chamar diferentes métodos privados. Talvez o autor desejasse ter métodos "pequenos" e extraísse parte do código para um método privado com nome inteligente.
Independentemente de como o método público é escrito, seu código de teste deve cobrir todos os caminhos. Se você descobrir após os testes que uma das instruções de ramificação (if / switch) em um método privado nunca foi abordada em seus testes, você terá um problema. Você perdeu um caso e a implementação está correta OU a implementação está errada e esse ramo nunca deveria ter existido de fato.
É por isso que uso muito a Cobertura e a NCover, para garantir que o meu teste de método público também abranja métodos privados. Sinta-se livre para escrever bons objetos OO com métodos particulares e não deixe o TDD / Testing entrar no seu caminho nesse sentido.
fonte
Seu exemplo ainda é perfeitamente testável, desde que você use a Injeção de Dependência para fornecer as instâncias com as quais seu CUT interage. Em seguida, você pode usar uma simulação, gerar os eventos de interesse e observar se a CUT executa ou não as ações corretas em suas dependências.
Por outro lado, se você tiver um idioma com bom suporte para eventos, poderá seguir um caminho ligeiramente diferente. Não gosto quando os objetos se inscrevem nos próprios eventos; em vez disso, a fábrica que cria o objeto liga os eventos aos métodos públicos do objeto. É mais fácil testar e torna visível externamente que tipos de eventos os CUT precisam ser testados.
fonte
Você não precisa abandonar o uso de métodos privados. É perfeitamente razoável usá-los, mas, de uma perspectiva de teste, é mais difícil testar diretamente, sem quebrar o encapsulamento ou adicionar código específico de teste às suas classes. O truque é minimizar as coisas que você sabe que farão seu intestino se contorcer porque você sente que sujou seu código.
Essas são as coisas que eu tenho em mente para tentar obter um equilíbrio viável.
Pense lateralmente. Mantenha suas classes pequenas e seus métodos menores e use muita composição. Parece mais trabalho, mas, no final, você acabará com itens testáveis individualmente, seus testes serão mais simples, você terá mais opções para usar zombarias simples no lugar de objetos reais, grandes e complexos, espero que bem: código fatorado e pouco acoplado e, mais importante, você terá mais opções. Manter as coisas pequenas tende a economizar tempo no final, porque você reduz o número de coisas que precisa verificar individualmente em cada classe e tende a reduzir naturalmente o espaguete de código que às vezes pode acontecer quando uma classe fica grande e tem muitas coisas. comportamento de código interdependente internamente.
fonte
Como esse objeto reage a esses eventos? Presumivelmente, ele deve invocar métodos em outros objetos. Você pode testá-lo verificando se esses métodos são chamados. Faça com que ele chame um objeto simulado e então você pode facilmente afirmar que ele faz o que você espera.
O problema é que queremos apenas testar a interação do objeto com outros objetos. Não nos importamos com o que está acontecendo dentro de um objeto. Portanto, você não deveria ter mais métodos públicos do que antes.
fonte
Eu lutei com esse mesmo problema também. Realmente, a maneira de contornar isso é: Como você espera que o resto do seu programa faça interface com essa classe? Teste sua classe de acordo. Isso forçará você a projetar sua classe com base em como o restante do programa interage com ela e, de fato, incentivará o encapsulamento e o bom design de sua classe.
fonte
Em vez de uso privado modificador padrão. Em seguida, você pode testar esses métodos individualmente, não apenas em conjunto com métodos públicos. Isso requer que seus testes tenham a mesma estrutura de pacotes que seu código principal.
fonte
internal
em .net.Alguns métodos particulares geralmente não são um problema. Você acabou de testá-los através da API pública como se o código estivesse embutido nos seus métodos públicos. Um excesso de métodos privados pode ser um sinal de baixa coesão. Sua classe deve ter uma responsabilidade coesa e, muitas vezes, as pessoas tornam métodos privados para dar a aparência de coesão onde realmente não existe.
Por exemplo, você pode ter um manipulador de eventos que faz muitas chamadas ao banco de dados em resposta a esses eventos. Como é obviamente uma prática recomendada instanciar um manipulador de eventos para fazer chamadas ao banco de dados, a tentação é tornar todos os métodos privados de todas as chamadas relacionadas ao banco de dados, quando elas realmente devem ser extraídas para uma classe separada.
fonte
TDD não está em desacordo com o encapsulamento. Tome o exemplo mais simples de um método ou propriedade getter, dependendo do idioma de sua escolha. Digamos que eu tenho um objeto Customer e quero que ele tenha um campo Id. O primeiro teste que vou escrever é um que diz algo como "customer_id_initializes_to_zero". Defino o getter para lançar uma exceção não implementada e assisto ao teste falhar. Então, a coisa mais simples que posso fazer para passar no teste é fazer com que o getter retorne zero.
A partir daí, vou para outros testes, presumivelmente aqueles que envolvem o ID do cliente como um campo funcional real. Em algum momento, provavelmente tenho que criar um campo privado que a classe do cliente use para acompanhar o que deve ser retornado pelo getter. Como exatamente eu acompanho isso? É um int simples apoio? Eu mantenho o controle de uma string e a converto em int? Eu mantenho o controle de 20 polegadas e a média delas? O mundo exterior não se importa - e seus testes TDD não se importam. Esse é um detalhe encapsulado .
Acho que isso nem sempre é óbvio imediatamente ao iniciar o TDD - você não está testando o que os métodos fazem internamente - está testando preocupações menos granulares da classe. Portanto, você não está procurando testar esse método
DoSomethingToFoo()
instancia uma barra, chama um método, adiciona duas a uma de suas propriedades, etc. Você está testando que, depois de alterar o estado do seu objeto, algum acessador de estado mudou (ou não). Esse é o padrão geral de seus testes: "quando eu faço X na minha classe em teste, posso observar Y subsequentemente". Como chega a Y não é uma preocupação dos testes, e é isso que está encapsulado e é por isso que o TDD não está em desacordo com o encapsulamento.fonte
Evite usar? Não.
Evite começar com ? Sim.
Percebo que você não perguntou se não há problema em ter aulas abstratas com TDD; se você entender como as classes abstratas emergem durante o TDD, o mesmo princípio também se aplica aos métodos privados.
Você não pode testar métodos diretamente em classes abstratas, assim como não pode testar métodos privados diretamente, mas é por isso que você não começa com classes abstratas e métodos privados; você começa com classes concretas e APIs públicas e depois refatora a funcionalidade comum à medida que avança.
fonte