O ciclo Vermelho - Verde - Refatorador para TDD está bem estabelecido e aceito. Escrevemos um teste de unidade com falha e fazemos com que seja aprovado da maneira mais simples possível. Quais são os benefícios dessa abordagem ao escrever muitos testes de unidade com falha para uma classe e fazer com que todos passem de uma só vez.
A suíte de testes ainda protege você contra a gravação de código incorreto ou cometer erros no estágio de refatoração, então qual é o problema? Às vezes, é mais fácil escrever todos os testes de uma classe (ou módulo) primeiro como uma forma de "despejo cerebral" para anotar rapidamente todo o comportamento esperado de uma só vez.
Respostas:
O design orientado a teste é sobre como acertar sua API , não o código.
O benefício de escrever os testes com falha mais simples primeiro é que você obtém sua API (que é basicamente o que você está projetando em tempo real) da maneira mais simples possível. Na frente.
Quaisquer usos futuros (que são os próximos testes que você escreve) passarão do design simples inicial, em vez de um design subótimo para lidar com casos mais complexos.
fonte
Quando você escreve um teste, você se concentra em uma coisa.
Com muitos testes, você concentra sua atenção em muitas tarefas, portanto não é uma boa ideia.
fonte
Uma das dificuldades ao escrever testes de unidade é que você está escrevendo um código e que por si só pode ser propenso a erros. Também há a possibilidade de você precisar alterar seus testes posteriormente, como resultado de um esforço de refatoração ao escrever seu código de implementação. Com o TDD, isso significa que você pode acabar se empolgando demais com seus testes e precisa reescrever muitos códigos de teste essencialmente "não testados" à medida que sua implementação amadurece ao longo do projeto. Uma maneira de evitar esse tipo de problema é simplesmente se concentrar em fazer uma única coisa de cada vez. Isso garante que você minimize o impacto de quaisquer alterações em seus testes.
Obviamente, isso se resume principalmente à maneira como você escreve seu código de teste. Você está escrevendo um teste de unidade para cada método individual ou está escrevendo testes focados em recursos / requisitos / comportamentos? Outra abordagem poderia ser usar uma abordagem orientada a comportamentos, com uma estrutura adequada, e concentrar-se em escrever testes como se fossem especificações. Isso significaria adotar o método BDD ou adaptar o teste BDD, se você desejar manter o TDD mais formalmente. Como alternativa, você pode se ater inteiramente ao paradigma TDD, mas alterar a maneira como escreve testes para que, em vez de se concentrar inteiramente nos métodos de teste individualmente, teste comportamentos mais geralmente como um meio de satisfazer as especificidades dos recursos de requisitos que você está implementando.
Independentemente da abordagem específica adotada, em todos os casos que descrevi acima, você usa uma abordagem de teste primeiro; portanto, embora possa ser tentador simplesmente baixar seu cérebro em um adorável conjunto de testes, você também deseja combater o tentação de fazer mais do que é absolutamente necessário. Sempre que estou prestes a iniciar um novo conjunto de testes, começo a repetir o YAGNI para mim mesmo e às vezes até o coloco em um comentário no meu código para me lembrar de manter o foco no que é imediatamente importante e de fazer apenas o mínimo necessário para satisfazer as requisitos do recurso que estou prestes a implementar. Aderir ao Red-Green-Refactor ajuda a garantir que você faça isso.
fonte
Eu acho que, ao fazer isso, você perde o processo de TDD. Ao escrever todos os seus testes no início, você não está realmente passando pelo processo de desenvolvimento usando o TDD. Você está simplesmente adivinhando de antemão quais testes precisará. Esse será um conjunto de testes muito diferente daqueles que você acaba escrevendo, se os fizer um de cada vez, à medida que desenvolve seu código. (A menos que seu programa seja trivial por natureza.)
fonte
Eu “escrevo” todos os testes que consigo pensar de frente enquanto assalto o cérebro, mas escrevo cada teste como um único comentário que descreve o teste.
Em seguida, converto um teste em código e faço o trabalho para que ele seja compilado e aprovado . Freqüentemente, decido que não preciso de todos os testes que pensei ter feito, ou preciso de testes diferentes; essas informações são provenientes da escrita do código para que os testes sejam aprovados.
O problema é que você não pode escrever um teste no código até ter criado o método e as classes testadas, caso contrário, você obterá muitos erros do compilador que lhe permitirão trabalhar em um único teste de cada vez.
Agora, se você estiver usando um sistema como fluxo de especificações quando os testes forem escritos em “inglês”, convém que os clientes concordem com um conjunto de testes enquanto você tiver o tempo deles, em vez de criar apenas um único teste.
fonte
A idéia por trás do TDD são iterações rápidas.
Se você tiver grandes faixas de testes que precisam ser gravadas antes de escrever seu código, é difícil refatorar iterativamente o código.
Sem a refatoração fácil de código, você perde muitos dos benefícios do TDD.
fonte
Na minha experiência (limitada) com TDD, posso dizer-lhe que toda vez que eu quebro a disciplina de escrever um teste de cada vez, as coisas correm mal. É uma armadilha fácil de cair. "Oh, esse método é trivial", você pensa consigo mesmo, "então eu vou nocautear esses dois outros testes relacionados e seguir em frente". Bem, adivinhe? Nada é tão trivial quanto parece. Toda vez que caí nessa armadilha, acabava depurando algo que achava fácil, mas havia casos estranhos nos cantos. E desde que eu escrevi vários testes ao mesmo tempo, foi muito trabalhoso rastrear onde estava o erro.
Se você precisar de um despejo de informações no cérebro, terá várias opções:
Observe que em nenhum lugar nesta lista está o compilador. :-)
fonte
Você está assumindo que sabe como será o seu código antes de escrevê-lo. TDD / BDD é tanto um processo de design / descoberta quanto um processo de controle de qualidade. Para um determinado recurso, você escreve o teste mais simples que verifica se o recurso é satisfeito (às vezes isso pode exigir vários devido à complexidade de um recurso). O primeiro teste que você escreve é carregado com suposições de como será o código de trabalho. Se você escrever todo o conjunto de testes antes de escrever a primeira linha de código para apoiá-lo, estará fazendo uma série de suposições não verificadas. Em vez disso, escreva uma suposição e verifique-a. Então escreva o próximo. No processo de verificação da próxima suposição, você pode apenas quebrar uma suposição anterior para voltar e alterar essa primeira suposição para corresponder à realidade ou alterar a realidade para que a primeira suposição ainda se aplique.
Pense em cada teste de unidade que você escreve como uma teoria em um caderno científico. Ao preencher o caderno, você prova suas teorias e forma novas. Às vezes, provar uma nova teoria desmente uma teoria anterior, então você deve corrigi-la. É mais fácil provar uma teoria de cada vez do que tentar provar digamos 20 de uma vez.
fonte
O TDD é uma abordagem altamente iterativa, que (na minha experiência) se encaixa melhor nas formas de desenvolvimento do mundo real. Normalmente, minha implementação toma forma gradualmente durante esse processo, e cada etapa pode trazer mais perguntas, idéias e idéias para teste. Isso é ideal para manter minha mente focada na tarefa real e é muito eficiente, porque eu só preciso manter um número limitado de coisas na memória de curto prazo a qualquer momento. Isso, por sua vez, reduz a possibilidade de erros.
Sua idéia é basicamente uma abordagem Big Test Up Front, com a qual o IMHO é mais difícil de lidar e pode se tornar mais inútil. E se você perceber, no meio do trabalho, que sua abordagem não é boa, sua API é falha e você precisa começar tudo de novo ou usar uma biblioteca de terceiros? Então, grande parte do trabalho realizado para escrever seus testes antecipadamente se torna um esforço desperdiçado.
Dito isto, se isso funcionar para você, tudo bem. Eu posso imaginar que, se você trabalha com uma especificação técnica fixa e detalhada, em um domínio com experiência íntima e / ou em uma tarefa relativamente pequena, você pode ter a maioria ou todos os casos de teste necessários prontos e sua implementação clara desde o começo. Então, pode fazer sentido começar escrevendo todos os testes de uma só vez. Se sua experiência é que isso o torna mais produtivo a longo prazo, você não precisa se preocupar muito com os livros de regras :-)
fonte
Além de apenas pensar em uma coisa, um paradigma do TDD é escrever o mínimo de código possível para passar no teste. Quando você escreve um teste de cada vez, é muito mais fácil ver o caminho para escrever código apenas o suficiente para que o teste seja aprovado. Com todo um conjunto de testes a serem aprovados, você não encontra o código em pequenas etapas, mas precisa dar um grande salto para que todos passem de uma só vez.
Agora, se você não se limitar a escrever o código para fazer com que todos passem "de uma só vez", mas escreva apenas o código suficiente para passar um teste de cada vez, ainda pode funcionar. Você precisaria ter mais disciplina para não apenas prosseguir e escrever mais código do que o necessário. Depois de iniciar esse caminho, você se abre para escrever mais código do que os testes descrevem, o que pode ser não testado , pelo menos no sentido de que não é conduzido por um teste e talvez no sentido de que não é necessário (ou exercido) por qualquer teste.
Descobrir o que o método deve fazer, como comentários, histórias, uma especificação funcional, etc., é perfeitamente aceitável. Eu esperaria para traduzi-los em testes, um de cada vez.
A outra coisa que você pode perder escrevendo os testes de uma só vez é o processo de raciocínio pelo qual a aprovação em um teste pode fazer com que você pense em outros casos de teste. Sem um banco de testes existentes, você precisa pensar no próximo caso de teste no contexto do último teste aprovado. Como eu disse, ter uma boa idéia do que o método deve fazer é muito bom, mas muitas vezes me vi encontrando novas possibilidades que não havia considerado a priori, mas que só ocorreram no processo de escrever o testes. Existe o perigo de você sentir falta deles, a menos que tenha o hábito específico de pensar em quais novos testes posso escrever que ainda não possuo.
fonte
Eu trabalhei em um projeto em que os desenvolvedores que escreveram os testes (com falha) eram diferentes dos desenvolvedores que implementavam o código necessário para fazê-los passar e eu achei realmente eficaz.
Nesse caso, apenas os testes relacionados à iteração atual foram gravados uma vez. Então, o que você sugere é perfeitamente possível nesse tipo de cenário.
fonte
fonte
O ciclo Red-Green-Refactor é uma lista de verificação destinada a desenvolvedores novos no TDD. Eu diria que é uma boa idéia seguir esta lista de verificação até que você saiba quando segui-la e quando você pode quebrá-la (ou seja, até que você saiba que não precisa fazer essa pergunta no stackoverflow :)
Tendo feito o TDD por quase uma década, posso dizer que raramente, se é que alguma vez, escrevo muitos testes com falha antes de escrever o código de produção.
fonte
Você está descrevendo o BDD, onde algumas partes interessadas externas têm uma especificação executável. Isso pode ser benéfico se houver uma especificação inicial predeterminada (por exemplo, uma especificação de formato, padrão industrial ou onde o programador não seja o especialista em domínio).
A abordagem normal é cobrir gradualmente mais e mais testes de aceitação, que é o progresso visível para o gerente de projeto e o cliente.
Você geralmente tem esses testes especificados e executados em uma estrutura BDD como Cucumber, Fitnesse ou algo parecido.
No entanto, isso não é algo que você confunde com seus testes de unidade, que estão muito mais próximos dos detalhes da implementação, com muitos casos de borda relacionados à API, problemas de inicialização, etc., fortemente focados no item em teste , que é um artefato de implementação .
A disciplina de refatorar verde-vermelho tem muitos benefícios, e a única vantagem que você pode esperar, digitando-os na frente, é empatar.
fonte
Um teste de cada vez: a principal vantagem é o foco em uma coisa. Pense no design mais profundo: você pode se aprofundar e manter o foco com um loop de feedback rápido. Você pode perder o escopo de todo o problema! É nesse momento que a refatoração (grande) entra em cena. Sem ele, o TDD não funciona.
Todos os testes: a análise e o design podem revelar mais sobre o escopo do problema. Pense no design amplo. Você analisa o problema de mais ângulos e adiciona informações da experiência. É inerentemente mais difícil, mas pode gerar benefícios interessantes - menos refatoração - se você fizer 'apenas o suficiente'. Cuidado, é fácil analisar demais e, no entanto, errar completamente o alvo!
Acho difícil recomendar geralmente preferir um ou outro, porque os fatores são muitos: experiência (especialmente com o mesmo problema), conhecimento e habilidades de domínio, compatibilidade do código para refatoração, complexidade do problema ...
Eu acho que se focarmos mais estreitamente em aplicativos de negócios típicos, o TDD com sua abordagem rápida de quase tentativa e erro geralmente venceria em termos de eficácia.
fonte
Supondo que sua estrutura de teste a suporte, o que eu sugeriria é que, em vez de implementar os testes que você deseja braindump, escreva testes pendentes descritivos que você implementará posteriormente. Por exemplo, se sua API deve fazer foo e bar, mas não biz, basta adicionar o código a seguir (este exemplo está em rspec) para seu conjunto de testes e atacá-los um por um. Você pensa rapidamente e pode resolver todos os seus problemas um por um. Quando todos os testes forem aprovados, você saberá quando resolveu todos os seus problemas durante o braindump.
fonte