É suficiente usar testes de aceitação e integração em vez de testes de unidade?


62

Breve introdução a esta pergunta. Eu tenho usado agora TDD e ultimamente BDD há mais de um ano. Uso técnicas como zombaria para tornar a escrita de meus testes mais eficiente. Ultimamente, comecei um projeto pessoal para escrever um programa de gerenciamento de dinheiro para mim. Como não tinha código legado, era o projeto perfeito para começar com o TDD. Infelizmente, não senti tanto a alegria do TDD. Até estragou tanto a minha diversão que desisti do projeto.

Qual era o problema? Bem, eu usei a abordagem do tipo TDD para permitir que os testes / requisitos evoluam o design do programa. O problema era que mais da metade do tempo de desenvolvimento era para testes de escrita / refatoração. Portanto, no final, eu não queria implementar mais recursos, porque precisaria refatorar e gravar em muitos testes.

No trabalho, tenho muito código legado. Aqui, escrevo cada vez mais testes de integração e aceitação e menos testes de unidade. Isso não parece ser uma abordagem ruim, pois os bugs são detectados principalmente pelos testes de aceitação e integração.

Minha ideia era que, no final, eu pudesse escrever mais testes de integração e aceitação do que testes de unidade. Como eu disse para detectar bugs, os testes de unidade não são melhores do que os testes de integração / aceitação. Teste de unidade também é bom para o design. Como eu escrevia muitas delas, minhas aulas são sempre projetadas para serem testáveis. Além disso, a abordagem para permitir que os testes / requisitos guiem o design leva, na maioria dos casos, a um design melhor. A última vantagem dos testes de unidade é que eles são mais rápidos. Eu escrevi testes de integração suficientes para saber que eles podem ser quase tão rápidos quanto os testes de unidade.

Depois de pesquisar na web, descobri que existem idéias muito semelhantes às minhas aqui e ali . O que você pensa dessa ideia?

Editar

Respondendo às perguntas, um exemplo em que o design foi bom, mas eu precisava de uma refatoração enorme para o próximo requisito:

No início, havia alguns requisitos para executar determinados comandos. Escrevi um analisador de comando extensível - que analisou comandos de algum tipo de prompt de comando e chamou o correto no modelo. O resultado foi representado em uma classe de modelo de exibição: Primeiro design

Não havia nada errado aqui. Todas as classes eram independentes uma da outra e eu podia facilmente adicionar novos comandos, mostrar novos dados.

O próximo requisito era que todo comando tivesse sua própria representação de vista - algum tipo de visualização do resultado do comando. Redesenhei o programa para obter um design melhor para o novo requisito: Segundo desenho

Isso também foi bom porque agora todos os comandos têm seu próprio modelo de visualização e, portanto, sua própria visualização.

O fato é que o analisador de comandos foi alterado para usar uma análise baseada em token dos comandos e foi retirado de sua capacidade de executar os comandos. Todo comando possui seu próprio modelo de visualização e o modelo de visualização de dados conhece apenas o modelo atual de visualização de comando que conhece os dados que devem ser mostrados.

Tudo o que eu queria saber neste momento é se o novo design não quebrou nenhum requisito existente. Não precisei alterar QUALQUER teste de aceitação. Eu tive que refatorar ou excluir quase TODOS os testes de unidade, o que foi uma enorme pilha de trabalho.

O que eu queria mostrar aqui é uma situação comum que acontecia frequentemente durante o desenvolvimento. Não houve nenhum problema com os designs antigos ou novos, eles mudaram naturalmente com os requisitos - como eu entendi, essa é uma vantagem do TDD, que o design evolui.

Conclusão

Obrigado por todas as respostas e discussões. No resumo desta discussão, pensei em uma abordagem que testarei no meu próximo projeto.

  • Antes de tudo, escrevo todos os testes antes de implementar qualquer coisa como sempre fiz.
  • Para requisitos, escrevo inicialmente alguns testes de aceitação que testam todo o programa. Depois, escrevo alguns testes de integração para os componentes nos quais preciso implementar o requisito. Se houver um componente que trabalhe em conjunto com outro componente para implementar esse requisito, eu também escreveria alguns testes de integração nos quais os dois componentes são testados juntos. Por último, mas não menos importante, se eu tiver que escrever um algoritmo ou qualquer outra classe com alta permutação - por exemplo, um serializador - eu escreveria testes de unidade para essas classes específicas. Todas as outras classes não são testadas, mas quaisquer testes de unidade.
  • Para erros, o processo pode ser simplificado. Normalmente, um bug é causado por um ou dois componentes. Nesse caso, eu escreveria um teste de integração para os componentes que testam o bug. Se relacionado a um algoritmo, eu escreveria apenas um teste de unidade. Se não for fácil detectar o componente onde o erro ocorre, eu escreveria um teste de aceitação para localizar o erro - isso deve ser uma exceção.


Essas perguntas parecem abordar mais o problema de por que escrever testes. Quero discutir se escrever testes funcionais em vez de testes de unidade pode ser uma abordagem melhor.
Yggdrasil 13/01

por minha leitura, respostas em questão duplicado são principalmente sobre quando não testes -Unidade fazer mais sentido
mosquito

Esse primeiro link é em si uma duplicata. Pense que você quer dizer: programmers.stackexchange.com/questions/66480/…
Robbie Dee

As respostas do link de Robbie Dee são ainda mais sobre o porquê de testar.
Yggdrasil 13/01

Respostas:


37

Está comparando laranjas e maçãs.

Testes de integração, testes de aceitação, testes de unidade, testes de comportamento - todos são testes e ajudarão você a melhorar seu código, mas também são bem diferentes.

Vou examinar cada um dos diferentes testes na minha opinião e, esperançosamente, explicar por que você precisa de uma mistura de todos eles:

Testes de integração:

Simplesmente, teste se diferentes componentes de seu sistema se integram corretamente - por exemplo - talvez você simule uma solicitação de serviço da web e verifique se o resultado volta. Geralmente, eu usaria dados estáticos reais (ish) e dependências simuladas para garantir que eles possam ser verificados consistentemente.

Testes de aptidão:

Um teste de aceitação deve se correlacionar diretamente com um caso de uso de negócios. Pode ser enorme ("as transações são enviadas corretamente") ou pequenas ("o filtro filtra com êxito uma lista") - não importa; o que importa é que ele deve estar explicitamente vinculado a um requisito específico do usuário. Eu gosto de focar neles para o desenvolvimento orientado a testes, porque significa que temos um bom manual de referência de testes para histórias de usuários para dev e qa verificar.

Testes unitários:

Para pequenas unidades discretas de funcionalidade que podem ou não compor uma história de usuário individual por si só - por exemplo, uma história de usuário que diz que recuperamos todos os clientes quando acessamos uma página da web específica pode ser um teste de aceitação (simule acessar a web página e verificação da resposta), mas também pode conter vários testes de unidade (verifique se as permissões de segurança estão verificadas, verifique se a conexão com o banco de dados consulta corretamente, verifique se qualquer código que limita o número de resultados é executado corretamente) - todos esses são "testes de unidade" que não é um teste de aceitação completo.

Testes de comportamento:

Defina qual deve ser o fluxo de um aplicativo no caso de uma entrada específica. Por exemplo, "quando a conexão não puder ser estabelecida, verifique se o sistema tenta novamente a conexão". Novamente, é improvável que seja um teste de aceitação total, mas ainda permite que você verifique algo útil.

Tudo isso é, na minha opinião, através de muita experiência em escrever testes; Não gosto de me concentrar nas abordagens dos livros didáticos - em vez disso, concentre-se no que dá valor aos seus testes.


Na sua definição, presumo que o que quero dizer é escrever mais testes de comportamento (?) Do que testes de unidade. O teste de unidade para mim é um teste que testa uma única classe com todas as dependências zombadas. Há casos em que os testes de unidade são mais úteis. Ou seja, quando escrevo um algoritmo complexo. Então, eu tenho muitos exemplos com a saída esperada do algoritmo. Quero testar isso no nível da unidade, porque é realmente mais rápido que o teste de comportamento. Não vejo o valor de testar uma classe no nível da unidade, que possui apenas uma mão cheia de caminhos através da classe, que podem ser facilmente testados por um teste de comportamento.
Yggdrasil 13/01

14
Pessoalmente, acho que os testes de aceitação são os mais importantes, os testes de comportamento são importantes ao testar coisas como comunicação, confiabilidade e casos de erro e os testes de unidade são importantes ao testar pequenos recursos complexos (algoritmos seriam um bom exemplo disso)
Michael

Eu não sou tão firme com sua terminologia. Nós programamos um conjunto de programação. Lá sou responsável pelo editor gráfico. Meus testes testam o editor com serviços simulados do resto do conjunto e com interface do usuário simulada. Que tipo de teste seria esse?
Yggdrasil 13/01

11
Depende do que você está testando - você está testando recursos de negócios (testes de aceitação)? Você está testando a integração (testes de integração)? Você está testando o que acontece quando você clica em um botão (testes de comportamento)? Você está testando algoritmos (testes de unidade)?
Michael

4
"Não gosto de me concentrar nas abordagens dos livros didáticos - em vez disso, concentre-se no que dá valor aos seus testes" Ah, é verdade! A primeira pergunta a ser feita sempre é "que problema eu resolvo fazendo isso?". E projeto diferente pode ter um problema diferente para resolver!
Laurent Bourgault-Roy

40

TL; DR: Desde que atenda às suas necessidades, sim.

Estou desenvolvendo o ATDD (Acceptance Test Driven Development) há muitos anos. Pode ser muito bem sucedido. Há algumas coisas para estar ciente.

  • Os testes de unidade realmente ajudam a reforçar o COI. Sem testes de unidade, a responsabilidade é dos desenvolvedores para garantir que eles atendam aos requisitos de código bem escrito (na medida em que os testes de unidade conduzam código bem escrito)
  • Eles podem ser mais lentos e ter falhas falsas se você estiver realmente usando recursos que normalmente são ridicularizados.
  • O teste não identifica o problema específico como os testes de unidade. Você precisa fazer mais investigações para corrigir falhas de teste.

Agora os benefícios

  • Muito melhor cobertura de teste, abrange pontos de integração.
  • Garante que o sistema como um todo atenda aos critérios de aceitação, que é o objetivo principal do desenvolvimento de software.
  • Torna os grandes refatores muito mais fáceis, rápidos e baratos.

Como sempre, cabe a você fazer a análise e descobrir se essa prática é apropriada para sua situação. Ao contrário de muitas pessoas, não acho que haja uma resposta certa idealizada. Depende de suas necessidades e exigências.


8
Ponto excelente. É muito fácil tornar-se um pouco cadete do espaço sobre testes e escrever centenas de casos para obter um brilho quente de satisfação quando "passa" com cores vivas. Simplificando: se o seu software não faz o que precisa fazer do ponto de vista do USUÁRIO, você falhou no primeiro e mais importante teste.
Robbie Dee

Bom ponto para identificar o problema específico. Se eu tenho um requisito enorme, escrevo testes de aceitação que testam todo o sistema e depois escrevo testes que testam subtarefas de determinados componentes do sistema para atingir o requisito. Com isso, posso identificar na maioria dos casos o componente em que está o defeito.
Yggdrasil

2
"Os testes de unidade ajudam a aplicar o COI"?!? Tenho certeza de que você quis dizer DI lá em vez de IoC, mas de qualquer maneira, por que alguém iria querer reforçar o uso de DI? Pessoalmente, acho que, na prática, o DI leva a não-objetos (programação no estilo processual).
Rogério

Você pode dar sua opinião sobre a opção (melhor da IMO) de fazer AMBOS integração e teste de unidade versus o argumento de apenas realizar teste de integração? Sua resposta aqui é boa, mas parece enquadrar essas coisas como mutuamente exclusivas, o que não acredito que sejam.
starmandeluxe

@starmandeluxe Eles realmente não são mutuamente exclusivos. Pelo contrário, é uma questão do valor que você deseja derivar dos testes. Eu faria o teste de unidade em qualquer lugar onde o valor excedesse o custo de desenvolvimento / suporte para escrever os testes de unidade. ex. Certamente eu testaria unitariamente a função de juros compostos em um aplicativo financeiro.
dietbuddha

18

Bem, eu usei a abordagem do tipo TDD para permitir que os testes / requisitos evoluam o design do programa. O problema era que mais da metade do tempo de desenvolvimento dos testes de escrita / refatoração

Os testes de unidade funcionam melhor quando a interface pública dos componentes para os quais são usados ​​não muda com muita frequência. Isso significa que, quando os componentes já foram bem projetados (por exemplo, seguindo os princípios do SOLID).

Portanto, acreditar que um bom design apenas "evolui" de "executar" muitos testes de unidade em um componente é uma falácia. O TDD não é um "professor" para um bom design, ele só pode ajudar um pouco para verificar se certos aspectos do design são bons (especialmente para testabilidade).

Quando seus requisitos mudam e você precisa alterar as partes internas de um componente, isso interromperá 90% de seus testes de unidade; portanto, é necessário refatorá-los com muita frequência, pois o design provavelmente não foi tão bom.

Portanto, meu conselho é: pense no design dos componentes que você criou e como torná-los mais seguindo o princípio de abertura / fechamento. A idéia do último é garantir que a funcionalidade de seus componentes possa ser estendida posteriormente sem alterá-los (e, portanto, sem quebrar a API do componente usada pelos testes de unidade). Esses componentes podem (e devem ser) cobertos por testes de teste de unidade, e a experiência não deve ser tão dolorosa quanto você a descreveu.

Quando você não pode criar esse projeto imediatamente, os testes de aceitação e integração podem realmente ser um começo melhor.

EDIT: Às vezes, o design de seus componentes pode ser bom, mas o design dos testes de unidade pode causar problemas . Exemplo simples: você deseja testar o método "MyMethod" da classe X e escrever

    var x= new X();
    Assert.AreEqual("expected value 1" x.MyMethod("value 1"));
    Assert.AreEqual("expected value 2" x.MyMethod("value 2"));
    // ...
    Assert.AreEqual("expected value 500" x.MyMethod("value 500"));

(suponha que os valores tenham algum tipo de significado).

Suponha ainda que no código de produção haja apenas uma chamada X.MyMethod. Agora, para um novo requisito, o método "MyMethod" precisa de um parâmetro adicional (por exemplo, algo como context), que não pode ser omitido. Sem testes de unidade, seria necessário refatorar o código de chamada em apenas um lugar. Com testes de unidade, é preciso refatorar 500 lugares.

Mas a causa aqui não é o teste de unidade, é apenas o fato de que a mesma chamada para "X.MyMethod" é repetida várias vezes, não seguindo estritamente o princípio "Não se repita (DRY). Portanto, a solução aqui é colocar os dados de teste e os valores esperados relacionados em uma lista e executar as chamadas para "MyMethod" em um loop (ou, se a ferramenta de teste suportar os chamados "testes de unidade de dados", para usar esse recurso). o número de lugares a serem alterados nos testes de unidade quando a assinatura do método muda para 1 (em oposição a 500).

No seu caso no mundo real, a situação pode ser mais complexa, mas espero que você entenda. Quando seus testes de unidade usam uma API de componentes para a qual você não sabe se pode estar sujeito a alterações, reduza o número de chamadas para essa API no mínimo.


"Isso significa que, quando os componentes já foram bem projetados.": Concordo com você, mas como os componentes já podem ser projetados se você escrever os testes antes de escrever o código, e o código é o design? Pelo menos é assim que eu entendi o TDD.
Giorgio

2
@ Giorgio: na verdade, não importa se você escreve os testes primeiro ou mais tarde. Design significa tomar decisões sobre a responsabilidade de um componente, sobre a interface pública, sobre dependências (diretas ou injetadas), sobre o tempo de execução ou o comportamento do tempo de compilação, sobre mutabilidade, sobre nomes, sobre fluxo de dados, fluxo de controle, camadas, etc. design também significa adiar algumas decisões para o último momento possível. O teste de unidade pode mostrar indiretamente se seu projeto foi bom: se você costuma refatorar muitos deles depois que os requisitos mudam, provavelmente não era.
Doc Brown

@ Giorgio: um exemplo pode esclarecer: digamos que você tenha um componente X com o método "MyMethod" e 2 parâmetros. Usando TDD, você escreve X x= new X(); AssertTrue(x.MyMethod(12,"abc"))antes de realmente implementar o método. Usando o design inicial, você pode escrever class X{ public bool MyMethod(int p, string q){/*...*/}}primeiro e depois os testes. Nos dois casos, você tomou a mesma decisão de design. Se a decisão foi boa ou ruim, o TDD não informará.
Doc Brown

11
Concordo com você: sou um pouco cético quando vejo o TDD aplicado às cegas com a suposição de que ele produzirá automaticamente um bom design. Além disso, às vezes o TDD atrapalha se o design ainda não estiver claro: sou forçado a testar os detalhes antes de ter uma visão geral do que estou fazendo. Então, se eu entendi direito, nós concordamos. Acho que (1) o teste de unidade ajuda a verificar um design, mas o design é uma atividade separada e (2) o TDD nem sempre é a melhor solução, porque você precisa organizar suas idéias antes de começar a escrever testes e o TDD pode atrasá-lo. esta.
Giorgio

11
Em pouco tempo, os testes de unidade podem mostrar falhas no design interno de um componente. A interface, as condições pré e pós devem ser conhecidas antecipadamente, caso contrário, você não poderá criar o teste de unidade. Portanto, o design do componente, o que o componente faz, precisa ser executado antes que o teste de unidade possa ser gravado. Como isso ocorre - o design de nível inferior, o design detalhado ou o design interno ou o que você quiser chamar - pode ocorrer após a gravação do teste de unidade.
Maarten Bodewes

9

Sim, claro que é.

Considere isto:

  • um teste de unidade é um pequeno pedaço de teste direcionado que exercita um pequeno pedaço de código. Você escreve muitos deles para obter uma cobertura de código decente, para que todos (ou a maioria dos bits estranhos) sejam testados.
  • um teste de integração é uma peça grande e ampla de teste que exercita uma grande superfície do seu código. Você escreve alguns deles para obter uma cobertura de código decente, para que todos (ou a maioria dos bits inábeis) sejam testados.

Veja a diferença geral ....

O problema é de cobertura de código; se você pode realizar um teste completo de todo o seu código usando testes de integração / aceitação, não há problema. Seu código foi testado. Esse é o objetivo.

Eu acho que você pode precisar misturá-los, pois todo projeto baseado em TDD exigirá alguns testes de integração apenas para garantir que todas as unidades funcionem bem juntas (sei por experiência própria que uma base de código testada a 100% da unidade não necessariamente funciona quando você coloca todos juntos!)

O problema realmente se resume à facilidade de testar, depurar as falhas e corrigi-las. Algumas pessoas acham que seus testes de unidade são muito bons nisso, são pequenos e simples e as falhas são fáceis de ver, mas a desvantagem é que você precisa reorganizar seu código para se adequar às ferramentas de teste de unidade e escrever muitos deles. É mais difícil escrever um teste de integração para cobrir muito código, e você provavelmente precisará usar técnicas como o log para depurar falhas (no entanto, eu diria que é necessário fazer isso de qualquer maneira, não é possível testar falhas na unidade) quando estiver no local!).

De qualquer forma, você ainda recebe o código testado, só precisa decidir qual mecanismo combina melhor com você. (Eu usaria um pouco de mix, testaria os algoritmos complexos e integraria o resto).


7
Não é bem verdade ... Com os testes de integração, é possível ter dois componentes com erros, mas seus erros são cancelados no teste de integração. Não importa muito até o usuário final usa-lo de uma forma em que apenas um desses componentes é usado ...
Michael Shaw

11
Cobertura de código! = Testado - além de erros cancelando um ao outro, e os cenários em que você nunca pensou? O teste de integração é bom para o teste do caminho feliz, mas raramente vejo testes de integração adequados quando as coisas não estão indo bem.
Michael

4
@Ptolemy Acho que a raridade de 2 componentes de buggy cancelando um ao outro é muito, muito menor do que 2 componentes de trabalho que interferem um no outro.
gbjbaanb

2
@ Michael, então você simplesmente não se esforçou o suficiente nos testes, eu disse que é mais difícil fazer bons testes de integração, pois os testes precisam ser muito mais detalhados. Você pode fornecer dados incorretos em um teste de integração tão facilmente quanto em um teste de unidade. Teste de integração! = Caminho feliz. É sobre exercitar o máximo de código possível, é por isso que existem ferramentas de cobertura de código que mostram quanto do seu código foi exercido.
gbjbaanb

11
@ Michael Quando uso ferramentas como Cucumber ou SpecFlow corretamente, posso criar testes de integração que também testam exceções e situações extremas tão rapidamente quanto testes de unidade. Mas eu concordo que se uma classe tem muitas permutações, prefiro escrever um teste de unidade para essa classe. Mas isso será menos frequente do que ter aulas com apenas alguns caminhos.
Yggdrasil 14/01

2

Eu acho que é uma ideia horrível.

Como os testes de aceitação e o teste de integração tocam partes mais amplas do seu código para testar um destino específico, eles precisarão de mais refatoração ao longo do tempo, e não menos. Pior ainda, como cobrem amplas seções do código, aumentam o tempo que você gasta rastreando a causa raiz, já que você tem uma área mais ampla para pesquisar.

Não, você deve escrever mais testes de unidade, a menos que tenha um aplicativo ímpar que seja 90% da interface do usuário ou algo mais estranho para o teste de unidade. A dor que você está enfrentando não é dos testes de unidade, mas do desenvolvimento do primeiro teste. Geralmente, você deve gastar apenas 1/3 do seu tempo na maioria dos testes de escrita. Afinal, eles estão lá para atendê-lo, não vice-versa.


2
A principal carne que ouço contra o TDD é que ela interrompe o fluxo de desenvolvimento natural e aplica a programação defensiva desde o início. Se um programador já está sob pressão de tempo, é provável que queira apenas cortar o código e aperfeiçoá-lo mais tarde. Obviamente, cumprir um prazo arbitrário com código de buggy é uma economia falsa.
Robbie Dee

2
De fato, especialmente como "polir mais tarde" nunca parece realmente acontecer - todas as revisões que faço quando um desenvolvedor pressiona "ele precisa sair, faremos mais tarde" quando todos sabemos, isso não vai acontecer - técnica dívida = desenvolvedores falidos na minha opinião.
Michael

3
A resposta faz sentido para mim, não sei por que ela recebeu tantos pontos negativos. Para citar Mike Cohn: "O teste de unidade deve ser a base de uma sólida estratégia de automação de teste e, como tal, representa a maior parte da pirâmide. Os testes de unidade automatizados são maravilhosos porque fornecem dados específicos a um programador - existe um bug e ele está ativado linha 47 " mountaingoatsoftware.com/blog/…
guillaume31 14/01

4
@ guillaume31 Só porque um cara disse uma vez que eles são bons não significa que eles são bons. Na minha experiência, os erros NÃO são detectados por testes de unidade, porque foram alterados para o novo requisito antecipadamente. Também a maioria dos erros são erros de integração. Por isso, detecto a maioria deles com meus testes de integração.
Yggdrasil 20/01

2
@Yggdrasil Também posso citar Martin Fowler: "Se você falhar em um teste de alto nível, não apenas possui um bug no seu código funcional, mas também um teste de unidade ausente". martinfowler.com/bliki/TestPyramid.html De qualquer forma, se apenas os testes de integração funcionarem para você, tudo bem. Minha experiência é que, embora necessárias, elas são mais lentas, entregam mensagens de falha menos precisas e menos manobráveis ​​(mais combinatórias) que os testes de unidade. Além disso, tendem a ser menos à prova de futuro quando escrevo testes de integração - raciocínio sobre cenários pré- (mal?) Concebidos, em vez da correção de um objeto em si.
precisa saber é o seguinte

2

A "vitória" do TDD é que, uma vez que os testes foram escritos, eles podem ser automatizados. O outro lado é que ele pode consumir uma parte significativa do tempo de desenvolvimento. Se isso realmente atrasa todo o processo é discutível. O argumento é que o teste inicial reduz o número de erros a serem corrigidos no final do ciclo de desenvolvimento.

É aqui que o BDD entra, já que os comportamentos podem ser incluídos no teste de unidade, para que o processo seja, por definição, menos abstrato e mais tangível.

Claramente, se uma quantidade infinita de tempo estivesse disponível, você faria o maior número possível de testes de várias variedades. No entanto, o tempo é geralmente limitado e os testes contínuos são rentáveis ​​até certo ponto.

Tudo isso leva à conclusão de que os testes que fornecem mais valor devem estar na frente do processo. Isso, por si só, não favorece automaticamente um tipo de teste em detrimento de outro - mais que cada caso deve ser considerado por seus méritos.

Se você estiver escrevendo um widget de linha de comando para uso pessoal, estará interessado principalmente em testes de unidade. Enquanto um serviço da web, por exemplo, exigiria uma quantidade substancial de integração / teste comportamental.

Embora a maioria dos tipos de teste se concentre no que poderia ser chamado de "linha de corrida", ou seja, testando o que é exigido pelas empresas hoje em dia, o teste de unidade é excelente para eliminar erros sutis que podem surgir em fases posteriores de desenvolvimento. Como esse é um benefício que não pode ser facilmente medido, geralmente é ignorado.


11
Escrevo meus testes antecipadamente e escrevo testes suficientes para cobrir a maioria dos erros. Quanto aos erros que aparecem mais tarde. Parece-me que um requisito mudou ou um novo requisito entra em jogo. Do que os testes de integração / comportamento precisam ser alterados, acrescentou. Se um bug for mostrado em um requisito antigo, meu teste para isso o mostrará. Quanto à automação. todos os meus testes são executados o tempo todo.
Yggdrasil 13/01

O exemplo em que eu estava pensando no último parágrafo é onde, digamos, uma biblioteca era usada exclusivamente por um único aplicativo, mas havia um requisito comercial para torná-la uma biblioteca de uso geral. Nesse caso, pode ser melhor que você faça pelo menos alguns testes de unidade em vez de escrever novos testes de integração / comportamento para todos os sistemas que você conectar à biblioteca.
Robbie Dee

2
Os testes de automação e de unidade são uma questão completamente ortogonal. Qualquer projeto que se preze terá integração automatizada e testes funcionais. Concedido, muitas vezes você não vê testes de unidade manuais, mas eles podem existir (basicamente o teste de unidade manual é um utilitário de teste para algumas funcionalidades específicas).
Jan Hudec

Bem, de fato. Há um mercado florescente de ferramentas automatizadas de terceiros que existem fora da esfera de desenvolvimento há um tempo considerável.
Robbie Dee

1

A última vantagem dos testes de unidade é que eles são mais rápidos. Eu escrevi testes de integração suficientes para saber que eles podem ser quase tão rápidos quanto os testes de unidade.

Este é o ponto chave, e não apenas "a última vantagem". Quando o projeto se torna cada vez maior, seus testes de aceitação de integração estão se tornando cada vez mais lentos. E aqui, eu quero dizer tão lento que você vai parar de executá-los.

Obviamente, os testes de unidade também estão se tornando mais lentos, mas ainda são mais rápidos do que a ordem de magnitude. Por exemplo, no meu projeto anterior (c ++, cerca de 600 kLOC, 4000 testes de unidade e 200 testes de integração), levou cerca de um minuto para executar todos e mais de 15 para executar testes de integração. Para criar e executar testes de unidade para a peça que está sendo alterada, levaria menos de 30 segundos, em média. Quando você pode fazê-lo tão rápido, você desejará fazê-lo o tempo todo.

Só para esclarecer: não digo para não adicionar testes de integração e aceitação, mas parece que você fez o TDD / BDD de maneira errada.

Teste de unidade também é bom para o design.

Sim, projetar com a testabilidade em mente tornará o design melhor.

O problema era que mais da metade do tempo de desenvolvimento era para testes de escrita / refatoração. Portanto, no final, eu não queria implementar mais recursos, porque precisaria refatorar e gravar em muitos testes.

Bem, quando os requisitos mudam, você precisa alterar o código. Eu diria que você não terminou seu trabalho se não escreveu testes de unidade. Mas isso não significa que você deve ter 100% de cobertura com testes de unidade - esse não é o objetivo. Algumas coisas (como GUI, ou acessar um arquivo, ...) nem sequer são feitas para serem testadas em unidade.

O resultado disso é uma melhor qualidade do código e outra camada de teste. Eu diria que vale a pena.


Também tivemos vários testes de aceitação de 1000, e levaria uma semana inteira para executar todos.


11
Você já olhou para o meu exemplo? Isso acontecia o tempo todo. O ponto é que, quando implemento um novo recurso, altero / adiciono o teste de unidade para que eles testem o novo recurso - portanto, nenhum teste de unidade será quebrado. Na maioria dos casos, tenho efeitos colaterais das alterações que não são detectadas pelos testes de unidade - porque o ambiente é ridicularizado. Na minha experiência por causa disso, nenhum teste de unidade me disse que eu quebrei um recurso existente. Sempre foram os testes de integração e aceitação que me mostraram meus erros.
Yggdrasil 17/01

Quanto ao tempo de execução. Com uma aplicação crescente, tenho principalmente um número crescente de componentes isolados. Caso contrário, eu fiz algo errado. Quando eu implemento um novo recurso, ele é principalmente apenas em um número limitado de componentes. Escrevo um ou mais testes de aceitação no escopo de todo o aplicativo - o que pode ser lento. Além disso, escrevo os mesmos testes do ponto de vista dos componentes - esses testes são rápidos, porque os componentes geralmente são rápidos. Eu posso executar os testes de componentes o tempo todo, porque eles são rápidos o suficiente.
Yggdrasil 17/01

@Yggdrasil Como eu disse, os testes de unidade não são todos poderosos, mas geralmente são a primeira camada de testes, pois são os mais rápidos. Outros testes também são úteis e devem ser combinados.
BЈовић

11
Só porque eles são mais rápidos, não significa que eles devem ser usados ​​apenas por isso ou porque é comum escrevê-los. Como eu disse, meus testes de unidade não são interrompidos - portanto, eles não têm nenhum valor para mim.
Yggdrasil 17/01

11
A questão era: qual o valor que tenho do teste de unidade quando eles não quebram? Por que me preocupar em escrevê-los, quando eu sempre preciso ajustá-los aos novos requisitos? O único valor deles que vejo é para algoritmos e outras classes com alta permutação. Mas estes são inferiores aos testes de componente e aceitação.
Yggdrasil 18/01
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.