TDD, novos testes enquanto antigos ainda não implementados

13

Estou experimentando o desenvolvimento orientado a teste e descobri que frequentemente chego à seguinte situação:

  1. Escrevo testes para algumas funcionalidades X. Esses testes falham.
  2. Ao tentar implementar o X, vejo que preciso implementar algum recurso Y em uma camada inferior do meu código. Então...
  3. Escrevo testes para Y. Agora, ambos os testes para X e Y falham.

Uma vez que eu tinha quatro recursos em diferentes camadas de código sendo trabalhados ao mesmo tempo, estava perdendo meu foco no que realmente estava fazendo (muitos testes falharam ao mesmo tempo).

Eu acho que poderia resolver isso colocando mais esforço no planejamento de minhas tarefas antes mesmo de começar a escrever testes. Mas, em alguns casos, eu não sabia que precisaria ir mais fundo, porque, por exemplo, não conhecia muito bem a API da camada inferior.

O que devo fazer nesses casos? O TDD tem alguma recomendação?

liori
fonte

Respostas:

9

O bom é que você percebe que seu código em teste precisa de assistência. Em vez de implementá-lo imediatamente, crie uma interface e use zombarias para garantir que seus testes estejam etiquetando o código correto. Após a aprovação dos testes, você poderá implementar o código em que se baseia.

Michael Brown
fonte
Meus testes geralmente não têm conhecimento sobre o que um método deve fazer internamente (por exemplo, qual API de nível inferior chamar). Devo apenas ajustar os testes para zombar do que for necessário no código testado?
Liori
2
Da mesma forma, suas classes testadas não devem se importar com o que as 'camadas inferiores' fazem. Use zombarias / stubs no lugar de classes / objetos reais. Isso pode exigir um pouco mais de esforço no design, mas resulta em código menos acoplado e mais fácil de reutilizar.
Mchl
1
Você está usando injeção de dependência? É assim que você pode separar facilmente as preocupações de nível inferior das classes de nível superior. Sua classe em teste possui um construtor com parâmetros para suas dependências (como interfaces). No seu teste, você cria simulações para as interfaces. Basicamente, você está fingindo que já implementou os serviços de nível inferior.
Michael Brown
@ Mike Brown, sim, eu faço. Eu sei que posso criar objetos simulados. Mas então, no meu teste de recurso X, tenho que saber de que parte das dependências Xpreciso zombar. Sinto que isso faz parte dos detalhes da implementação, que não devem fazer parte dos testes - caso contrário, talvez seja necessário alterar os testes enquanto refatoramos a implementação. Devo me preocupar com isso?
Liori
1
De maneira alguma ... os testes devem refletir as suposições do sistema em teste. Também ajuda a expor o que você precisa dos serviços nos quais o sistema depende. Eu costumava concordar com você sobre esse assunto, mas comparo-o com a forma como entendi a programação recursiva. Primeiro, você escreve o código assumindo que possui uma função que faz o que deseja. Então você escreve o código que faz o que deseja.
Michael Brown
4

Stubs e zombarias podem ser usados ​​para simular a funcionalidade que ainda não está sendo modificada / implementada. Eles também podem ajudá-lo a resolver as dependências que causam esse tipo de 'reação em cadeia'.

Por outro lado, talvez manter apenas um teste (com falha) que conduza a próxima mudança seja a melhor abordagem.

Outros testes direcionados ao código que depende de novas funcionalidades podem ser desabilitados temporariamente, pois não são realmente relevantes neste momento. no seu caso, desative os testes para o X até implementar Y etc.

Dessa forma, você pode manter o foco na próxima mudança, que é o que você quer, eu acho.

Ratkok
fonte
Ah, procurei um recurso para desativar um teste durante uma execução de teste dentro do meu IDE e não o encontrei. Agora eu descobri que o python unittestjá tem pular teste. Isso pode ser suficiente para mim.
Liori
Usamos a estrutura C ++ do teste do google - e ela tem uma opção para desativar os testes. Os testes desativados não são executados, mas compilados - no momento em que você precisar deles - eles estão prontos para execução (além disso, você pode 'forçar a execução' de testes desativados - tipo de 'ativação em tempo de execução') - recurso excelente ...
ratkok
3

Pare

De antemão, parece que pode haver dois problemas separados aqui:

  1. você esqueceu algumas histórias e cenários de teste e não as descobriu até começar a trabalhar em um cenário de teste específico e / ou

  2. você está realmente fazendo testes de unidade, e não testes de recursos TDD

Para o número 1, pare , volte e atualize as histórias e os cenários de teste e comece novamente com um cenário diferente.

Na segunda posição, pare e lembre-se de que você está testando recursos, não unidades, portanto, use simulações para encobrir outras interfaces e / ou implementar mais código para fazer o teste passar sem adicionar novos cenários de teste. Isso pressupõe que você não esteja perdendo cenários de teste, mas sim - e isso é realmente comum - testes de unidade conflitantes e TDD.

Steven A. Lowe
fonte
Eu realmente gosto da sua resposta, é melhor explicar o que realmente está acontecendo.
Maple_shaft
... Com isso dito, não conheço um PM no mundo que não perca completamente a cabeça com a frase "PARE, precisamos voltar atrás". Eles tentarão de tudo, menos sacrificar o primogênito no altar para manter o projeto em andamento, dívidas técnicas e testes de unidade incompletos. Eu acho que você não pode culpá-los quando a única métrica deles em uma organização é concluir o projeto no prazo. Algumas organizações valorizam mais o tempo do que a qualidade e é por isso que provavelmente nunca vi o TDD funcionar com sucesso nesses tipos de organizações, o que infelizmente é a maioria delas IMO.
Maple_shaft
@maple_shaft: a quantidade de tempo que você para para se reagrupar pode ser de apenas algumas horas - a menos que seu processo esteja muito distante, nesse caso, parar por alguns dias para colocá-lo de volta nos trilhos tornará muito mais provável que o projeto terá sucesso. Não faz sentido ir a todo vapor na trilha errada!
Steven A. Lowe
0

Esta é uma ótima pergunta e uma enorme frustração para mim também com o TDD. Sinto que o TDD não tem nesse cenário em que você simplesmente não tem como saber quais componentes ou recursos de nível inferior serão necessários até começar a desenvolver.

Pessoalmente, descobri que o TDD só funciona se você sabe exatamente o que precisa fazer e o que precisa chamar para executar um recurso. Os desenvolvedores nem sempre sabem tudo antes de começarmos, então descobri que a melhor maneira de mitigar a mesma situação que você descreve:

Protótipo

Quando faço aplicativos protótipos simples para explorar e descobrir métodos e abordagens para um problema técnico, descubro muito do trabalho de perna e removo essa pesquisa antes de começar. Projetar e estimar também é muito mais fácil.

Se o protótipo precisar estar tão envolvido que se torne o aplicativo, peço que você não faça a coisa preguiçosa no entanto e crie testes de unidade para o seu protótipo após o fato.

Nesse momento, você deve saber mais sobre a API de nível inferior e conseguir zombar da API de nível inferior em seus componentes de nível superior.

maple_shaft
fonte
Então, na verdade, você está sugerindo obter mais informações para a fase de planejamento, executando alguma codificação exploratória de maneira informal (= não seguindo alguma metodologia formalizada). E então assuma que ele fornecerá informações suficientes para planejar o código real. Estou certo?
Liori
Por que você supõe que a prototipagem é um processo informal? Todas as estimativas devem levar em consideração a criação de protótipos e os cronogramas do projeto, assim como uma tarefa de desenvolvimento necessária. Eu vejo o mesmo que Design ou Code-Review. Nessa nota, é formalizado e deve ser considerado, ainda mais em tarefas com muitas incógnitas. Sem a criação de protótipos e a capacidade de executar a Prova de Conceito, a busca pelo TDD pressupõe que os desenvolvedores saibam TUDO sobre QUALQUER COISA com TODOS os recursos. O mundo real não funciona assim e eu não me importo com o quão inteligente ou experiente você é.
maple_shaft
Por "caminho informal", não quis dizer que o tempo para a criação de protótipos não deva ser considerado, mas, enquanto você faz protótipos, não segue o TDD ou qualquer outra metodologia de código.
Liori
TDD é uma metodologia para testes e desenvolvimento de unidades. Faria sentido fazer o TDD for Code Review? O TDD faz sentido para o design, a redação de especificações técnicas ou o quadro branco? A prototipagem é uma tarefa em si, um tipo exploratório de desenvolvimento para pesquisa, prova de conceito e educação.
maple_shaft
1
O TDD faz todo o sentido para a criação de protótipos. Ele permite que você exponha rapidamente o que quer que seja (objeto, função, API, programa inteiro) na forma de um conjunto de requisitos executáveis ​​e repetíveis. Faça um favor a si mesmo e leia Growing Object Oriented Software Guided by Tests ; leva você passo a passo através da construção de um aplicativo inteiro (incluindo integração) em uma maneira de testar primeiro.
Frank Shearar
0

Depende do tipo de teste que você escreve enquanto faz TDD.

O modelo clássico é escrever testes de unidade e usar zombarias ou stubs para separar o teste das outras "unidades" do código.

Existem muitos outros modelos alternativos, como o ATDD, onde o teste é feito em uma pilha cheia ou quase em uma pilha cheia. Nesse caso específico, seus testes de escrita que afirmam que o comportamento exigido do programa não é uma única unidade de código, portanto você não escreveria outros testes. Você implementaria a ida e volta para satisfazer o teste. Você adiciona outros testes para outros recursos / comportamentos.

dietbuddha
fonte