Os testes para desenvolvimento orientado a testes (TDD) são sempre testes unitários?

41

Compreendo o desenvolvimento orientado a testes até o momento em que você só pode escrever código produtivo quando tiver um teste de unidade (vermelho) com falha. Com base nisso, tenho a pergunta se a abordagem orientada a testes também pode ser aplicada a outras formas de testes.

user1364368
fonte
6
Não é incomum usar mais de um nível diferente de vermelho / verde / refator aninhado um dentro do outro. Por exemplo, você pode seguir o vermelho / verde / refatorar enquanto escreve testes de aceitação / comportamento, em que a fase 'verde' do próprio teste de aceitação contém várias iterações vermelhas / verdes / refatores dos testes de unidade.
Sean Burton
1
O título não corresponde ao conteúdo da pergunta. O título "são testes sempre testes unitários " (resposta: não, pode haver outros tipos de testes além dos testes unitários), o conteúdo pergunta "você deve escrever o teste primeiro?".
AnoE
@AnoE A primeira frase do conteúdo é apenas uma declaração introdutória. A frase segundos não pergunta se o teste deve ser escrito primeiro, mas se a abordagem TDD pode ser usada para métodos de teste diferentes do TDD.
precisa saber é o seguinte
@ user1364368, sinta-se à vontade para reformular um pouco a pergunta, pelo menos fiquei confuso sobre qual é sua intenção na primeira leitura e a pergunta mais votada, enquanto abordar as duas frases também começa com destaque na primeira.
AnoE
@AnoE Mudei o início da segunda frase para deixar claro qual é a pergunta real.
precisa saber é o seguinte

Respostas:

27

Tudo o que o TDD exige de você é que você escreva um teste com falha e modifique seu código para fazê-lo passar.

Normalmente, os "testes de unidade" são pequenos e rápidos e testam parte de seu código isoladamente. Por serem rápidos, também faz o loop vermelho / verde / refatorar rapidamente. No entanto, eles sofrem apenas com peças de teste isoladas. Então você precisa de outros testes também (integração, aceitação etc). Ainda é uma boa prática seguir os mesmos princípios: escreva um teste com falha e modifique o código para fazê-lo funcionar. Esteja ciente de que eles geralmente são mais lentos, pois isso pode afetar o tempo do ciclo de vermelho / verde / refatorar.

David Arno
fonte
59

O ciclo do refator verde vermelho baseia-se em um princípio muito sólido:

Confie apenas nos testes que você viu passar e falhar.

Sim, isso também funciona com testes de integração automatizados. Também testes manuais. Heck, ele funciona em testadores de bateria de carro. É assim que você testa o teste.

Alguns acham que os testes de unidade cobrem a menor coisa que pode ser testada. Alguns pensam em algo que é rápido para testar. O TDD é mais do que apenas o ciclo do refator verde vermelho, mas essa parte tem um conjunto muito específico de testes: não são os testes que você idealmente executará uma vez antes de enviar uma coleção de alterações. São os testes que você executará toda vez que fizer alguma alteração. Para mim, esses são seus testes de unidade.

candied_orange
fonte
1
Essa é também uma das razões pelas quais os testes negativos são importantes ao testar a ocorrência de um erro: você deseja garantir que tudo funcionaria, com dois testes (um produzindo o erro exato esperado, o outro não produzindo o erro) ao lado de um ao outro ajuda a aumentar a confiança de que, no futuro, esse estado vermelho / verde continuará.
Matthieu M.
12

No entanto, estou me perguntando se a abordagem orientada a testes também pode ser aplicada a outras formas de testes.

Sim, e uma abordagem bem conhecida que faz isso é o desenvolvimento orientado pelo comportamento . Os testes gerados a partir das especificações formais no BDD podem ser chamados de "testes de unidade", mas normalmente não são tão baixos quanto no TDD real, provavelmente se ajustam melhor ao termo "testes de aceitação".

Doc Brown
fonte
8

Compreendo o desenvolvimento orientado a testes até o momento em que você só pode escrever código produtivo quando tiver um teste de unidade (vermelho) com falha.

Não. Você só pode escrever o código mais simples possível para alterar a mensagem do teste. Não diz nada sobre que tipo de teste.

De fato, você provavelmente começará escrevendo um teste de aceitação com falha (vermelho) para um critério de aceitação; mais precisamente, você escreverá o teste de aceitação mais simples que pode falhar; depois de executar o teste, observe-o falhar e verifique se ele falha pelo motivo certo. Em seguida, você escreve um teste funcional com falha para obter uma fatia da funcionalidade desse critério de aceitação; novamente, você escreve o teste funcional mais simples que pode falhar, executa-o, observa-o falhar e verifica se ele falha pelo motivo certo. Em seguida, você escreve um teste de unidade com falha, o teste de unidade mais simples que pode falhar, executa-o, assista-o falhar, verifique se ele falha pelo motivo certo.

Agora , você escreve o código de produção mais simples que pode alterar a mensagem de erro. Execute o teste novamente, verifique se a mensagem de erro foi alterada, se foi na direção correta e se o código alterou a mensagem pelo motivo correto. (Idealmente, a mensagem de erro já deve ter saído e o teste deve passar, mas, na maioria das vezes, é melhor tomar pequenas etapas para alterar a mensagem em vez de tentar fazer o teste passar de uma só vez - essa é a razão por que os desenvolvedores de estruturas de teste gastam tanto esforço em suas mensagens de erro!)

Depois de passar no teste de unidade, você refatora seu código de produção sob a proteção de seus testes. (Observe que, no momento, o teste de aceitação e o teste funcional ainda estão falhando, mas tudo bem, já que você está refatorando apenas unidades individuais cobertas por testes de unidade.)

Agora você cria o próximo teste de unidade e repita o procedimento acima, até que o teste funcional também passe. Sob a proteção do teste funcional, agora você pode fazer refatorações em várias unidades.

Esse ciclo intermediário agora se repete até que o teste de aceitação seja aprovado; nesse momento, você pode fazer refatorações em todo o sistema.

Agora, você escolhe o próximo critério de aceitação e o ciclo externo inicia novamente.

Kent Beck, o "descobridor" do TDD (ele não gosta do termo "inventor", ele diz que as pessoas fazem isso o tempo todo, ele apenas deu um nome e escreveu um livro sobre ele) usa uma analogia da fotografia e chama isso de "aumentar e diminuir o zoom".

Nota: você nem sempre precisa de três níveis de testes. Talvez, às vezes você precise de mais. Mais frequentemente, você precisa de menos. Se suas peças de funcionalidade são pequenas e seus testes funcionais são rápidos, você pode passar sem (ou com menos testes de unidade). Muitas vezes, você só precisa de testes de aceitação e testes de unidade. Ou, seus critérios de aceitação são tão refinados que seus testes de aceitação são funcionais.

Kent Beck diz que, se ele tiver um teste funcional rápido, pequeno e focado, ele primeiro escreverá os testes de unidade, permitirá que os testes de unidade conduzam o código e exclua (alguns) os testes de unidade novamente que abrangem o código que também é coberto pelo teste funcional rápido. Lembre-se: o código de teste também é um código que precisa ser mantido e refatorado, quanto menos houver, melhor!

No entanto, estou me perguntando se a abordagem orientada a testes também pode ser aplicada a outras formas de testes.

Você realmente não aplica o TDD aos testes. Você o aplica a todo o processo de desenvolvimento. É isso que significa a parte "orientada" do Desenvolvimento Orientado a Testes : todo o seu desenvolvimento é orientado por testes. Os testes não apenas orientam o código que você escreve, mas também o código a ser escrito, o código a ser escrito a seguir. Eles dirigem o seu design. Eles dizem quando você termina. Eles dizem a você o que trabalhar a seguir. Eles falam sobre falhas de design no seu código (quando é difícil escrever testes).

Keith Braithwaite criou um exercício que ele chama de TDD como se você quisesse . Consiste em um conjunto de regras (baseadas nas Três Regras do TDD do tio Bob Martin , mas muito mais rigorosas) que você deve seguir rigorosamente e que foram projetadas para orientá-lo a aplicar o TDD com mais rigor. Funciona melhor com a programação de pares (para que seu par possa garantir que você não está infringindo as regras) e com um instrutor.

As regras são:

  1. Escreva exatamente um novo teste, o menor teste possível, que parece apontar na direção de uma solução
  2. Veja isso falhar; falhas de compilação contam como falhas
  3. Faça o teste de (1) passar escrevendo o menor código de implementação possível no método de teste .
  4. Refatorar para remover a duplicação e, se necessário, para melhorar o design. Seja rigoroso sobre o uso desses movimentos:
    1. você deseja um novo método - aguarde até o tempo de refatoração e, em seguida, crie novos métodos (sem teste) executando um destes procedimentos, e de nenhuma outra maneira:
      • de preferência: extraia o método no código de implementação criado como (3) para criar um novo método na classe de teste ou
      • se você deve: mover o código de implementação de acordo com (3) para um método de implementação existente
    2. você deseja uma nova classe - espere até o tempo de refatoração e, em seguida, crie classes que não são de teste para fornecer um destino para um método Move e por nenhum outro motivo
    3. preencha classes de implementação com métodos executando o método Move e de nenhuma outra maneira

Essas regras são destinadas ao exercício do TDD. Eles não foram feitos para realmente fazer TDD na produção (embora nada o impeça de testá-lo). Eles podem se sentir frustrantes porque às vezes parecerá que você dá milhares de pequenos passos sem fazer nenhum progresso real.

Jörg W Mittag
fonte
2

O TDD não se limita ao que a comunidade tradicional de Teste de Software chama de "teste de unidade". Esse mal-entendido muito comum é o resultado da infeliz sobrecarga de Kent Beck do termo "unidade" ao descrever sua prática de TDD. O que ele quis dizer com "teste de unidade" era um teste executado isoladamente. Não depende de outros testes. Cada teste deve configurar o estado de que precisa e fazer qualquer limpeza quando terminar. É nesse sentido que um teste de unidade no sentido TDD é uma unidade. É independente. Ele pode ser executado sozinho ou em conjunto com qualquer outro teste de unidade em qualquer ordem.

Referência : "Desenvolvimento Orientado a Testes por Exemplo", de Kent Beck

Kent Beck descreve o que ele entende por “teste de unidade” no Capítulo 32 - Domínio do TDD

Jason Desrosiers
fonte
1

Eu não li livros sobre isso nem sigo completamente as práticas TDD "padrão" o tempo todo, mas, na minha opinião, o ponto principal da filosofia do TDD, com o qual eu concordo completamente, é que você precisa definir o sucesso primeiro . Isso é importante em todos os níveis de design, em "Qual é o objetivo deste projeto?" para "Quais devem ser as entradas e saídas deste pequeno método?"

Existem várias maneiras de fazer essa definição de sucesso. Um método útil, especialmente para os métodos de baixo nível com potencialmente muitos casos extremos, é escrever testes em código. Para alguns níveis de abstração, pode ser útil apenas escrever uma nota rápida sobre o objetivo do módulo ou o que quer que seja, ou até mesmo verificar-se mentalmente (ou pedir a um colega de trabalho) para garantir que tudo faça sentido e esteja de acordo. lugar lógico. Às vezes, é útil descrever um teste de integração no código (e, é claro, que ajuda a automatizá-lo), e às vezes é útil apenas definir um plano de teste rápido razoável que você pode usar para garantir que todos os sistemas estejam trabalhando juntos da maneira que você estão esperando.

Mas, independentemente das técnicas ou ferramentas específicas que você está usando, na minha opinião, o principal a tirar da filosofia do TDD é que definir o sucesso acontece primeiro. Caso contrário, você está jogando o dardo e depois pintando o alvo em qualquer lugar em que aconteceu.


fonte
1

Na palestra Desenvolvimento Orientado a Testes: Não foi isso que quisemos dizer Steve Freeman mostra o seguinte slide do quadro geral do TDD (veja a imagem abaixo resposta). Isso inclui uma etapa "Escreva um teste de ponta a ponta com falha", que é seguida por "Escreva um teste de unidade com falha". (Clique para ampliar, no canto superior direito)

Portanto, não no TDD, os testes nem sempre são testes de unidade.

E sim, você pode (e talvez deva) começar com um teste de ponta a ponta de nível superior que falha antes de escrever seu primeiro teste de unidade. Este teste descreve o comportamento que você deseja alcançar. Isso gera cobertura em mais níveis da pirâmide de teste . Adrian Sutton explica a experiência do LMAX, que mostra que os testes de ponta a ponta podem desempenhar um papel grande e valioso .

insira a descrição da imagem aqui

Niels van Reijmersdal
fonte
-1

Não, não pode ser aplicado a outros tipos de testes, por uma simples razão prática: outros tipos de testes estão demorando muito para serem executados.

O ciclo TDD típico é: escrever teste com falha, implementar, refatorar o código. As etapas intermediárias são a construção e a execução de testes, e eles precisam ser extremamente rápidos. Se não estiverem, as pessoas começarão a pular as etapas e você não estará mais executando o TDD.

BЈовић
fonte
1
Isso está incorreto: dependendo do programa e do teste (e do idioma), os testes de integração de ponta a ponta podem ser executados facilmente em menos de 3 segundos. É perfeitamente possível executar um conjunto completo de testes de ponta a ponta em muito pouco tempo, mesmo que seja bem projetado. Então, "não posso" é bastante forte.
Jonathan fundido
@jcast Eu nunca vi outra coisa tão rápida. Meus testes funcionais no meu projeto anterior levaram 30 segundos, e isso é rápido. Integração ainda mais. No meu caso, nada mais fazia sentido. Além disso, os testes de unidade são os mais rápidos de todos os tipos de testes - portanto, faz sentido usá-los.
BЈовић