Eu estava discutindo testes de unidade / integração com um colega, e ele fez um caso interessante contra escrever testes de unidade. Sou um grande proponente do teste de unidade (principalmente JUnit), mas estou interessado em ouvir as opiniões de outras pessoas, pois ele fez alguns pontos interessantes.
Para resumir seus pontos:
- Quando ocorrem grandes alterações de código (novo conjunto de POJOs, refatoração de aplicativos importantes etc.), os testes de unidade tendem a ser comentados em vez de retrabalhados.
- O tempo é melhor gasto em testes de integração que abrangem casos de uso, o que torna os testes de menor escopo menos / nem um pouco importantes.
Pensamentos sobre isso? Ainda sou teste pró-unidade (como o vejo consistentemente produzindo código aprimorado), embora os testes de integração sejam pelo menos tão valiosos.
unit-testing
integration-tests
Jeff Levine
fonte
fonte
Respostas:
Eu costumo ficar do lado do seu amigo porque, com muita frequência, os testes de unidade estão testando as coisas erradas .
Os testes de unidade não são inerentemente ruins. Mas eles costumam testar os detalhes da implementação em vez do fluxo de entrada / saída. Você acaba com testes completamente inúteis quando isso acontece. Minha regra é que um bom teste de unidade diga que você acabou de quebrar alguma coisa; um teste de unidade ruim apenas diz que você acabou de mudar alguma coisa.
Um exemplo em cima da minha cabeça é um teste que foi escondido no WordPress há alguns anos. A funcionalidade sendo testada girava em torno de filtros que se chamavam e os testes estavam verificando se os retornos de chamada seriam chamados na ordem correta. Mas, em vez de (a) executar a cadeia para verificar se os retornos de chamada são chamados na ordem esperada, os testes se concentraram em (b) ler algum estado interno que, sem dúvida, não deveria ter sido exposto para começar. Mude os internos e (b) fica vermelho; enquanto (a) só fica vermelho se as alterações nos internos quebram o resultado esperado ao fazê-lo. (b) foi claramente um teste sem sentido na minha opinião.
Se você tem uma classe que expõe alguns métodos para o mundo exterior, a coisa correta a teste na minha opinião são os últimos métodos únicos . Se você testar a lógica interna também, poderá acabar expondo a lógica interna para o mundo exterior, usando métodos de teste complicados ou com uma infinidade de testes de unidade que invariavelmente quebram sempre que você deseja alterar alguma coisa.
Com tudo isso dito, eu ficaria surpreso se seu amigo fosse tão crítico sobre os testes de unidade em si, como você parece sugerir. Em vez disso, acho que ele é pragmático. Ou seja, ele observou que os testes de unidade que são escritos são praticamente inúteis na prática. Citação: "testes de unidade tendem a ser comentados em vez de retrabalhados". Para mim, há uma mensagem implícita - se eles tendem a precisar ser reformulados, é porque tendem a ser péssimos. Supondo que sim, a segunda proposição segue: os desenvolvedores perderiam menos tempo escrevendo código mais difícil de errar - ou seja, testes de integração.
Como tal, não se trata de alguém ser melhor ou pior. Só que é muito mais fácil errar e, na verdade, muitas vezes errado na prática.
fonte
Com um grupo indisciplinado de codificadores de cowboys que acham que todos os testes "verdes" são realizados quando você comenta todos os testes existentes: é claro. Para refazer os testes de unidade, é preciso ter a mesma disciplina que escrevê-los em primeira mão. Portanto, o que você precisa trabalhar aqui é a "definição de pronto" da sua equipe.
Isso não é completamente errado nem completamente certo. O esforço para escrever testes úteis de integração depende muito do tipo de software que você está escrevendo. Se você possui um software no qual os testes de integração podem ser quase tão facilmente escritos quanto os testes de unidade, eles ainda são rápidos e oferecem uma boa cobertura do seu código, então seu colega tem razão. Mas para muitos sistemas que eu já vi esses três pontos não podem ser facilmente cumpridos por testes de integração, é por isso que você deve se esforçar para ter também testes de unidade.
fonte
Não sei se aceito os argumentos do seu colega.
Existem outros argumentos mais convincentes contra testes de unidade. Bem, não exatamente contra eles. Comece com o dano do projeto induzido pelo teste do DHH . Ele é um escritor divertido, então leia-o. O que eu tirei disso:
Se você fizer grandes alterações no design para acomodar seus testes de unidade, eles podem atrapalhar outros objetivos em seu projeto. Se possível, pergunte a uma pessoa imparcial qual abordagem é mais fácil de seguir. Se seu código altamente testável for difícil de entender, você poderá perder todos os benefícios obtidos com o teste de unidade. A sobrecarga cognitiva de muita abstração retarda você e facilita os erros de vazamento. Não desconte.
Se você deseja ser sutil e filosófico, existem muitos recursos excelentes:
fonte
Não faça isso. Se você encontrar alguém que faça isso, mate-o e verifique se ele não se reproduz, pois seus filhos podem herdar esse gene defeituoso. A chave que eu vejo assistindo programas de televisão da CSI é espalhar muitas amostras de DNA enganosas por toda parte. Dessa maneira, você polui a cena do crime com tanto barulho que, dada a natureza dispendiosa e demorada dos testes de DNA, a probabilidade de que eles o fixem a você é astronomicamente baixa.
fonte
Nesse ponto, o processo de revisão de código diz "vá consertar os testes de unidade" e isso não é mais um problema :-) Se o processo de revisão de código não detectar esse tipo de falha importante no código enviado, então esse é o problema.
fonte
Eu sempre tento manter a refatoração e a mudança de funcionalidade separadas. Quando preciso fazer as duas coisas, costumo confirmar a refatoração primeiro.
Ao refatorar o código sem alterar a funcionalidade, os testes de unidade existentes devem ajudar a garantir que a refatoração não interrompa acidentalmente a funcionalidade. Portanto, para tal confirmação, consideraria desabilitar ou remover os testes de unidade como um grande sinal de aviso. Qualquer desenvolvedor que faça isso deve ser instruído a não fazer isso quando o código estiver sendo revisado.
É possível que alterações que não alteram a funcionalidade ainda causem falhas nos testes de unidade devido a testes de unidade defeituosos. Se você entende o código que está mudando, a causa dessas falhas no teste de unidade geralmente é imediatamente óbvia e fácil de corrigir.
Por exemplo, se uma função usa três argumentos, um teste de unidade que cobre a interação entre os dois primeiros argumentos para a função pode não ter tido o cuidado de fornecer um valor válido para o terceiro argumento. Essa falha no teste de unidade pode ser exposta por uma refatoração do código testado, mas é fácil de corrigir se você entender o que o código deve fazer e o que o teste de unidade está testando.
Ao alterar a funcionalidade existente, geralmente será necessário também alterar alguns testes de unidade. Nesse caso, os testes de unidade ajudam a garantir que seu código altere a funcionalidade conforme pretendido e não tenha efeitos colaterais indesejados.
Ao corrigir erros ou adicionar novas funcionalidades, geralmente é necessário adicionar mais testes de unidade. Para aqueles, pode ser útil confirmar os testes de unidade primeiro e confirmar a correção de bug ou a nova funcionalidade posteriormente. Isso facilita a verificação de que os novos testes de unidade não foram aprovados no código mais antigo, mas foram aprovados no código mais recente. Essa abordagem não é totalmente sem inconvenientes, portanto, também existem argumentos a favor da confirmação de novos testes de unidade e atualizações de código simultaneamente.
Há algum elemento de verdade nisso. Se você puder obter cobertura das camadas inferiores da pilha de software com testes direcionados para as camadas superiores da pilha de software, seus testes poderão ser mais úteis ao refatorar o código.
Eu não acho que você encontrará um acordo sobre a distinção exata entre um teste de unidade e um teste de integração. E não me preocuparia se você tivesse um caso de teste que um desenvolvedor chamasse de teste de unidade e outro chamasse de teste de integração, desde que concordem que é um caso de teste útil.
fonte
Testes de integração são feitos para verificar se o sistema se comporta como deveria, o que sempre tem valor comercial. Os testes de unidade nem sempre fazem isso. Os testes de unidade podem estar testando código morto, por exemplo.
Existe uma filosofia de teste que diz que qualquer teste que não forneça informações é um desperdício. Quando um pedaço de código não está sendo trabalhado, seus testes de unidade sempre (ou quase sempre) ficam verdes, fornecendo pouca ou nenhuma informação.
Todo método de teste estará incompleto. Mesmo se você obtiver 100% de cobertura de alguma métrica, ainda não estará analisando quase todos os conjuntos possíveis de dados de entrada. Portanto, não pense que os testes de unidade são mágicos.
Eu sempre vi a afirmação de que ter testes de unidade melhora seu código. Na minha opinião, as melhorias que os testes de unidade trazem ao código estão forçando as pessoas que não estão acostumadas a quebrar seu código em pedaços pequenos e bem-fatorados. Se você se acostumar a projetar seu código em partes pequenas e bem-fatoradas normalmente, poderá achar que não precisa de testes de unidade para forçá-lo a fazer isso.
Dependendo da intensidade do seu teste de unidade e do tamanho do seu programa, você pode acabar com uma enorme quantidade de testes de unidade. Nesse caso, grandes mudanças se tornam mais difíceis. Se uma grande mudança causar muitos testes de unidade com falha, você poderá se recusar a fazer a alteração, fazer muito trabalho para corrigir muitos testes ou descartá-los. Nenhuma das opções é ideal.
Os testes de unidade são uma ferramenta na caixa de ferramentas. Eles estão lá para servi-lo, e não o contrário. Certifique-se de tirá-los pelo menos tanto quanto você colocar.
fonte
Os testes de unidade definitivamente não são a bala de prata que alguns proponentes afirmam ser. Mas, em geral, eles têm uma relação custo / benefício muito positiva. Eles não tornarão seu código "melhor", talvez mais modular. "melhor" tem muito mais fatores do que o que "testabilidade" implica. Mas eles garantirão que funcione em todos os casos e usos que você pensou e eliminarão a maioria dos seus erros (provavelmente os mais triviais).
O maior benefício dos testes de unidade é que eles tornam seu código mais flexível e resistente a alterações. Você pode refatorar ou adicionar recursos e ter certeza de que não quebrou nada. Isso por si só os faz valer a pena para a maioria dos projetos.
Referindo-se aos pontos apresentados por seu amigo:
Se adicionar POJOs interromper seus testes de unidade, eles provavelmente serão muito mal escritos. Os POJOs geralmente são o idioma que você está usando para falar sobre seu domínio, e os problemas que você está resolvendo, alterá-los obviamente significa toda a lógica de negócios e, provavelmente, o código da apresentação deve mudar. Você não pode esperar que o que garante que essas partes do seu programa funcionem não mude ...
Reclamar que os testes de unidade são ruins, porque as pessoas os comentam, é como reclamar dos cintos de segurança, porque as pessoas não os usam. Se alguém comentar o código de teste e quebrar alguma coisa, ele deve terminar em uma conversa muito séria e desagradável com seu chefe ...
Os testes de integração são muito superiores aos testes de unidade. sem dúvida. especialmente se você estiver testando um produto. Porém, escrever testes de integração bons e abrangentes, que proporcionará o mesmo valor, cobertura e garantia de correção que o teste de unidade oferece, é incrivelmente difícil.
fonte
Só consigo pensar em um argumento contra testes de unidade, e é bastante fraco.
Os testes de unidade mostram a maior parte de seu valor quando você refatorar ou alterar o código posteriormente. Você vê uma enorme redução no tempo necessário para adicionar recursos e garantir que não quebrou nada.
No entanto: no entanto, existe um custo (pequeno) no momento de escrever testes.
Portanto: se um projeto é suficientemente curto e não requer manutenção / atualizações contínuas, é possível que o custo de escrever testes supere seus benefícios.
fonte