É razoável não escrever testes de unidade porque eles tendem a ser comentados mais tarde ou porque os testes de integração são mais valiosos?

35

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.

Jeff Levine
fonte
9
Você mesmo disse: escrever testes de unidade produz um código melhor, porque obriga a escrever um código testável. O código testável por unidade possui muitas propriedades muito importantes que melhoram sua qualidade geral.
Robert Harvey
11
@ Robert Harvey - Concordo - eu estaria interessado em ver uma lista dessas propriedades.
Jeff Levine
18
Quanto ao primeiro ponto - você está basicamente dizendo que uma desvantagem dos testes de unidade é que eles não funcionam quando você não os usa. Você pode dizer o mesmo sobre qualquer outra prática. Além disso, seu uso da voz passiva está encobrindo o fato de alguém estar comentando ativamente os testes de unidade. Portanto, parece que você não tem adesão dos desenvolvedores da equipe, o que é um problema diferente.
JacquesB

Respostas:

62

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.

Denis de Bernardy
fonte
13
Isso, isso, cem vezes isso. Isso é ótimo no desenvolvimento orientado a testes. Escrever testes primeiro o ajudará a escrever testes que não estão testando detalhes de sua implementação, mas o comportamento pretendido que você estava tentando implementar.
Wirrbel 12/12/15
9
Penso que os testes de unidade frágeis não são uma característica inerente ao teste de unidade, mas em vez de entender mal para que serve o teste de unidade. O que precisamos aqui é de melhor educação para os desenvolvedores. Se você testar os detalhes da implementação da unidade, estará fazendo errado . Se você tornar métodos privados públicos "para testá-los", estará fazendo errado . Sempre teste a interface e o comportamento públicos, que devem mudar com menos frequência do que os detalhes da implementação!
Andres F.12 /
2
@ DocBrown: De fato não. O que eu quis dizer é que, muitas vezes, é feito errado que o colega do OP o corrige na maioria dos casos - ou pelo menos em todos os casos em que já encontrei empresas obcecadas por testes de unidade.
Denis de Bernardy
3
@DenisdeBernardy: meu argumento é: tudo o que você escreveu é certamente correto com muita sabedoria da prática, mas estou perdendo uma conclusão sobre o que isso significa para o caso dos OPs com seu colega, no contexto da sugestão de "testes de integração como o melhor alternativa ".
Doc Brown
5
Eu concordo plenamente com Doc Brown. Essencialmente, sua posição parece ser de que os testes de unidade são bons, mas quando são mal escritos, tornam-se um aborrecimento. Portanto, você não concorda com o colega do OP, apenas que deve certificar-se de que realmente escreve testes de unidade antes de afirmar sua utilidade. Acho que sua resposta é muito confusa, pois a primeira frase afirma que você concorda com o colega da OP quando isso não parece ser o caso. Parece mais um discurso retórico sobre testes de unidade mal escritos (que é um problema na prática, admito), não um caso contra testes de unidade em geral.
Vincent Savard
38

Quando ocorrem grandes alterações no código, os testes de unidade tendem a ser comentados em vez de retrabalhados.

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.

O tempo é melhor gasto em testes de integração, cobrindo casos de uso que tornam os testes de menor escopo menos / nem um pouco importantes.

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.

Doc Brown
fonte
Foi assim que me senti quando estávamos discutindo. Supondo que testes de integração completos e relevantes possam ser gravados para um caso de uso, parece que o teste de unidade seria um ponto discutível. (Embora agora que escrevo isso, estou pensando em casos futuros inesperados - como nosso back-end sendo transformado em um serviço da Web para outro aplicativo).
Jeff Levine
+1 Para "você precisa trabalhar na sua definição de concluído". Bem dito!
12135 Andres F.12:
14

Não sei se aceito os argumentos do seu colega.

  1. Se os testes de unidade são comentados, é porque alguém está sendo preguiçoso. Isso não é culpa dos testes de unidade. E se você começar a usar o preguiçoso como desculpa, poderá argumentar contra praticamente qualquer prática que exija um pouco de esforço.
  2. Se você está fazendo certo, os testes de unidade não precisam demorar muito. Eles não o impedem de fazer um trabalho mais importante. Se todos concordarmos que a correção de código quebrado é mais importante do que qualquer outra coisa, um teste de unidade é muito importante. É uma maneira fácil de testar as condições pós. Sem ele, como você sabe que cumpriu as garantias pós-condição? E se você não tiver, seu código está quebrado.

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:

Roger escasso
fonte
+1 para esse artigo, pode ser uma resposta melhor para o caso dos OPs do que tudo o que podemos escrever aqui- #
Doc Brown
+1 no ponto 1, sobre a preguiça ser uma desculpa. Não posso enfatizar o suficiente como isso é frustrante. Eu estive lá. Recentemente, na verdade.
Greg Burghardt
3
O motivo 1 não é culpa dos testes de unidade, mas ainda é um motivo contra os testes de unidade.
user253751
Depois de ler o artigo do DHH, verifique os vídeos em que ele discutiu o tópico com Kent Beck e Martin Fowler (eles estão no youtube) - muitas pessoas parecem ter uma versão mais extrema do ponto de vista do que ele pretendia e ele refina um pouco nessas discussões.
Jules
6

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.

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
4

testes de unidade tendem a ser comentados em vez de retrabalhados.

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.

Philip Kendall
fonte
3

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.

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.

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.

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.

Kasperd
fonte
3

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.

Michael Shaw
fonte
3

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:

  1. 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 ...

  2. 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 ...

  3. 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.

AK_
fonte
2

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.

Ewan
fonte
+1 por tentar responder sinceramente à pergunta que foi feita.
RubberDuck
2
O custo do teste de unidade definitivamente não é pequeno. Os bons testes de unidade geralmente levam mais tempo para escrever do que o que eles testam. Na maioria das vezes, os benefícios excedem o custo.
AK_
11
apenas em teoria - quando você refatorar, você acaba tendo um custo enorme atualizando os testes de unidade também, pois eles quase sempre são muito granulares. Se você estava falando em ter um bom conjunto de testes de integração, estaria certo.
Gbjbaanb
Se um projeto é curto o suficiente e não requer manutenção contínua / atualizações - a maioria dos sistemas legados têm começou como curta projeto sem testes - e acabar com a contratação de mais desenvolvedores para manter o sistema crescido sem testes
Fabio