Hoje estávamos treinando TDD e descobrimos o seguinte ponto de incompreensão.
A tarefa é a soma de retorno de entrada "1,2" dos números que é 3. O que escrevi (em C #) foi:
numbers = input.Split(',');
return int.Parse(numbers[0]) + int.Parse(numbers[1]); //task said we have two numbers and input is correct
Mas outros caras preferiram fazer de outra maneira. Primeiro, para a entrada "1,2", eles adicionaram o seguinte código:
if (input == "1,2")
return 3;
Em seguida, eles introduziram mais um teste para a entrada "4,5" e alteraram a implementação:
if (input == "1,2")
return 3;
else if (input == "4,5")
return 9;
E depois disso eles disseram "Ok, agora vemos o padrão" e implementaram o que eu fiz inicialmente.
Acho que a segunda abordagem se encaixa melhor na definição de TDD, mas ... devemos ser tão rigorosos quanto a isso? Para mim, não há problema em pular etapas triviais do bebê e combiná-las em "twinsteps" se tiver certeza de que não vou pular nada. Estou errado?
Atualizar. Cometi um erro ao não esclarecer que não era o primeiro teste. Já havia alguns testes, então "retorno 3" na verdade não era o código mais simples para satisfazer o requisito.
Respostas:
Escreva o código mais simples que faz os testes passarem.
Nenhum de vocês fez isso, tanto quanto eu posso ver.
Passo 1 do bebê.
Teste: Para a entrada "1,2", retorne a soma dos números que é 3
Faça o teste falhar:
Faça o teste passar:
Passo 2 do bebê.
Teste: Para a entrada "1,2", retorne a soma dos números, que é 3
Teste: para a entrada "4,5", retorne a soma dos números, que é 9
O segundo teste falha, então faça-o passar:
(Muito mais simples que uma lista de se ... retornar)
Certamente, você pode argumentar sobre a implementação óbvia neste caso, mas se você estava falando sobre fazê-lo estritamente nas etapas do bebê, essas são as etapas corretas, IMO.
O argumento é que, se você não escrever o segundo teste, poderá surgir alguma faísca mais tarde e "refatorar" seu código para ler:
E, sem executar as duas etapas, você nunca fez o segundo teste ficar vermelho (o que significa que o teste em si é suspeito).
fonte
input.Length
não é tão buscado, especialmente se a entrada para o método for uma medida de algum arquivo em algum lugar e você inadvertidamente chamou seu métodoSize()
.Eu acho que a segunda maneira é mente estupidamente estúpida. Vejo o valor de dar passos pequenos o suficiente, mas escrever esses pequenos passos de zigoto (não posso nem chamá-los de bebê) é apenas estúpido e uma perda de tempo. Especialmente se o problema original que você está resolvendo já é muito pequeno por si próprio.
Eu sei que é treinamento e é mais sobre mostrar o princípio, mas acho que esses exemplos fazem o TDD mais mal do que bem. Se você quiser mostrar o valor dos passos do bebê, use pelo menos um problema em que haja algum valor nele.
fonte
Kent Beck aborda isso em seu livro Test Driven Development: By Example.
Seu exemplo indica uma ' implementação óbvia ' - você deseja retornar a soma dois valores de entrada, e este é um algoritmo bastante básico a ser alcançado. Seu contra-exemplo cai em 'fingir até você fazer' (embora seja um caso muito simples).
A implementação óbvia pode ser muito mais complicada do que isso - mas basicamente entra em ação quando a especificação de um método é bastante rígida - por exemplo, retorna uma versão codificada em URL de uma propriedade de classe - você não precisa perder tempo com várias codificações falsificadas.
Uma rotina de conexão com o banco de dados, por outro lado, precisaria de um pouco mais de reflexão e teste, para que não haja uma implementação óbvia (mesmo que você já tenha escrito várias vezes em outros projetos).
Do livro:
fonte
Eu vejo isso como seguindo a letra da lei, mas não seu espírito.
Seus passos de bebê devem ser:
Além disso, o verbo no método é
sum
não é uma soma, é um teste para entradas específicas.
fonte
Para mim, parece bom combinar várias etapas triviais de implementação em uma ligeiramente menos trivial - eu faço isso o tempo todo também. Eu não acho que é preciso ser religioso sobre seguir o TDD todas as vezes ao pé da letra.
OTOH, isso se aplica apenas a etapas realmente triviais, como no exemplo acima. Para qualquer coisa mais complexa, que eu não possa ter em mente ao mesmo tempo e / ou onde não tenho 110% de certeza sobre o resultado, prefiro dar um passo de cada vez.
fonte
Ao iniciar o TDD pela primeira vez, o tamanho das etapas pode ser um problema confuso, como ilustra essa pergunta. As perguntas que eu costumava fazer quando comecei a escrever aplicativos orientados a teste eram: O teste que estou escrevendo está ajudando a impulsionar o desenvolvimento de meus aplicativos? Isso pode parecer trivial e não relacionado a alguns, mas fica aí comigo por um momento.
Agora, quando me propus a escrever qualquer aplicativo, normalmente começarei com um teste. Quanto de uma etapa desse teste está amplamente relacionada ao meu entendimento do que estou tentando fazer. Se eu acho que tenho praticamente o comportamento de uma classe na minha cabeça, o passo será grande. Se o problema que estou tentando resolver for muito menos claro, talvez a etapa seja simplesmente que eu saiba que está indo para um método chamado X e que retornará Y. Nesse ponto, o método nem sequer terá parâmetros e existe uma chance de que o nome do método e o tipo de retorno sejam alterados. Nos dois casos, os testes estão impulsionando meu desenvolvimento. Eles estão me dizendo coisas sobre minha inscrição:
Essa classe que eu tenho na minha cabeça vai realmente funcionar?
ou
Como diabos eu vou fazer isso?
O ponto é que eu posso alternar entre grandes passos e pequenos passos em um piscar de olhos. Por exemplo, se um grande passo não funcionar e eu não conseguir ver uma maneira óbvia de contornar, mudarei para um passo menor. Se isso não funcionar, mudarei para uma etapa ainda menor. Depois, existem outras técnicas, como a triangulação, se eu ficar realmente preso.
Se, como eu, você é um desenvolvedor e não um testador, o objetivo do TDD não é escrever testes, mas escrever código. Não se preocupe em escrever vários testes pequenos, se não receberem nenhum benefício.
Espero que você tenha gostado do seu treinamento com TDD. IMHO se mais pessoas foram infectadas, então o mundo seria um lugar melhor :)
fonte
Em uma cartilha sobre testes de unidade, li a mesma abordagem (etapas que parecem realmente muito pequenas) e como resposta à pergunta "quão pequenas devem ser" algo que eu gostei, que foi (parafraseado) assim:
É sobre como você está confiante de que as etapas funcionam. Você pode dar grandes passos, se quiser. Mas, tente por algum tempo e você encontrará muita confiança equivocada em lugares que você considera como garantidos. Portanto, os testes ajudam a criar uma confiança baseada em fatos.
Então, talvez seu colega seja um pouco tímido :)
fonte
Não é o ponto principal que a implementação do método é irrelevante, desde que os testes tenham êxito? A extensão dos testes falhará mais rapidamente no segundo exemplo, mas poderá falhar nos dois casos.
fonte
Estou de acordo com as pessoas que dizem que nem a implementação é a mais simples.
A razão pela qual a metodologia é tão rigorosa é que ela obriga a escrever o maior número possível de testes relevantes. Retornar um valor constante para um caso de teste e chamá-lo de aprovado é bom, porque força você a voltar e especificar o que realmente deseja para obter algo diferente do absurdo do seu programa. Usar um caso tão trivial é dar um tiro no pé em alguns aspectos, mas o princípio é que erros surgem nas lacunas de suas especificações quando você tenta fazer "demais" e diminui o requisito para a implementação mais simples possível, garantindo que um O teste deve ser escrito para cada aspecto exclusivo do comportamento que você realmente deseja.
fonte