Por que não escrever todos os testes ao mesmo tempo ao fazer TDD?

52

O ciclo Red - Green - Refactor para TDD é bem estabelecido e aceito. Escrevemos um teste de unidade com falha e o fazemos passar 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 eles passem de uma só vez?

O conjunto de testes ainda protege você contra escrever 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 para uma classe (ou módulo) primeiro como uma forma de 'brain dump' para anotar rapidamente todo o comportamento esperado de uma só vez.

    
por RichK 02.04.2012 / 15:32
fonte

16 respostas

45

O design orientado a testes significa obter a API correta, não o código.

O benefício de escrever primeiro os testes mais simples é que você obtém sua API (que essencialmente você está projetando em tempo real) da forma mais simples possível. Na frente.

Quaisquer usos futuros (os próximos testes que você escreve são) irão desde o design simples inicial, ao invés de um design sub-ótimo lidando com casos mais complexos.

    
por 02.04.2012 / 14:12
fonte
72

Quando você escreve um um teste, você se concentra em uma coisa .
Com muitos testes, você espalha sua atenção em muitas tarefas, por isso não é uma boa ideia.

    
por 02.04.2012 / 12:29
fonte
25

Uma das dificuldades ao escrever testes de unidade é que você está escrevendo código, e isso por si só pode ser propenso a erros. Existe também a possibilidade de que você pode acabar precisando alterar seus testes mais tarde como resultado de um esforço de refatoração à medida que você escreve seu código de implementação. Com o TDD, isso significa que você poderia acabar ficando um pouco empolgado demais com seus testes e descobrir que precisa reescrever um código de teste essencialmente "não testado" à medida que sua implementação amadurece ao longo do projeto. Uma maneira de evitar esse tipo de problema é simplesmente concentrar-se em fazer uma única coisa de cada vez. Isso garante que você minimize o impacto de qualquer alteração nos seus testes.

Naturalmente, isso se resume a como você escreve seu código de teste. Você está escrevendo um teste de unidade para cada método individual ou está escrevendo testes que enfocam características / requisitos / comportamentos? Outra abordagem poderia ser usar uma abordagem orientada por comportamento com uma estrutura adequada e concentrar-se em escrever testes como se fossem especificações. Isso significaria ou adotar o método do BDD ou adaptar o teste do BDD se você quiser manter o TDD mais formalmente. Alternativamente, você pode ficar totalmente com o paradigma TDD, mas alterar a maneira como você escreve os testes para que, em vez de se concentrar inteiramente nos métodos de teste individualmente, teste os comportamentos de maneira mais geral para satisfazer as especificidades dos recursos de requisitos que você está implementando. p> Independentemente da abordagem específica que você toma, em todos os casos que descrevi acima, você está usando uma abordagem de teste em primeiro lugar, portanto, embora seja tentador simplesmente fazer o download do seu cérebro em uma suíte de testes, você também quer para combater a tentação de fazer mais do que o absolutamente necessário. Sempre que estou prestes a começar uma nova suíte de testes, começo a repetir YAGNI para mim mesmo e às vezes até faço um comentário em meu código para me lembrar de manter o foco naquilo que é imediatamente importante e fazer o mínimo necessário para satisfazer o requisitos do recurso que estou prestes a implementar. Furar a Red-Green-Refactor ajuda a garantir que você faça isso.

    
por 26.04.2012 / 02:05
fonte
16

Acho que, ao fazer isso, você perde o processo de TDD. Por apenas 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 você precisará. Este será um conjunto de testes muito diferente dos que você acaba escrevendo, se você fizer um de cada vez enquanto desenvolve seu código. (A menos que seu programa seja trivial por natureza).

    
por 02.04.2012 / 14:33
fonte
10

A ideia por trás do TDD é uma interação rápida.

Se você tem grandes extensões de testes que precisam ser escritas antes de escrever seu código, é difícil refatorar seu código de maneira iterativa.

Sem a rápida refatoração de código, você perde muitos dos benefícios do TDD.

    
por 29.11.2013 / 14:56
fonte
9

Eu "escrevo" todos os testes que consigo pensar de antemão enquanto faço "brain storming", no entanto, escrevo cada teste como um único comentário descrevendo o teste.

Eu então converto um teste em código e faço o trabalho para que ele compile e passe . Muitas vezes eu decido que não preciso de todos os testes que achei que fiz, ou preciso de testes diferentes, essa informação só vem da escrita do código para fazer os testes passarem.

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 farão com que você trabalhe em um único teste por vez.

Agora, se você estiver usando um sistema como fluxo de especificação quando os testes forem escritos em "inglês", convém fazer com que os clientes concordem com um conjunto de testes enquanto você tem tempo, em vez de criar um único teste.

    
por 02.04.2012 / 13:50
fonte
5

Na minha experiência (limitada) com o TDD, posso dizer-lhe que a cada tempo eu quebrei a disciplina de escrever um teste de cada vez, as coisas correram mal. É uma armadilha fácil de cair. "Oh, esse método é trivial", você pensa para si mesmo, "então eu vou apenas derrubar esses dois outros testes relacionados e seguir em frente." Bem, adivinhe? Nada é tão trivial quanto parece. Toda vez que caí nessa armadilha, acabei depurando algo que eu achava fácil, mas acabei tendo casos esquisitos estranhos. E desde que comecei a escrever vários testes de uma só vez, foi muito trabalhoso rastrear onde estava o bug.

Se você precisar de informações, você tem muitas opções:

  • Quadro branco
  • Histórias de usuário
  • Comentários
  • Bom e velho papel e caneta

Observe que em nenhum lugar dessa lista o compilador. : -)

    
por 02.04.2012 / 15:43
fonte
5

Você está assumindo que sabe como será seu código antes de escrevê-lo. O 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 está satisfeito (às vezes isso pode exigir vários devido à complexidade de um recurso). Esse primeiro teste que você escreve é carregado com suposições de como o código de trabalho se parecerá. Se você escrever o conjunto de testes inteiro antes de escrever a primeira linha de código para suportá-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 simplesmente quebrar uma suposição anterior, de modo que você precisa voltar atrás e alterar essa primeira suposição para corresponder à realidade ou mudar a realidade, de modo que a primeira suposição ainda se aplique.

Pense em cada teste de unidade que você escreve como 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ê precisa consertá-la. É mais fácil provar uma teoria de cada vez do que tentar provar 20 de uma só vez.

    
por 02.04.2012 / 15:51
fonte
4

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, insights e ideias para testes. 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.

A sua ideia é basicamente uma abordagem do Big Test Up Front, que a IMHO é mais difícil de gerir e pode tornar-se mais dispendiosa. E se você perceber no meio do seu 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, muito do trabalho colocado para escrever seus testes na frente se torna um esforço desperdiçado.

Dito isto, se isso funcionar para você, tudo bem. Posso imaginar que, se você trabalhar com uma especificação técnica fixa e detalhada, em um domínio com o qual esteja intimamente experiente e / ou em uma tarefa relativamente pequena, poderá ter a maioria ou todos os casos de teste necessários 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 a sua experiência é que isso o torna mais produtivo a longo prazo, você não precisa se preocupar muito com os livros de regras: -)

    
por 02.04.2012 / 12:36
fonte
4

Além de apenas pensar em uma coisa, um paradigma de 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 suficiente para fazer o teste passar. Com um conjunto completo de testes para passar, você não entra no código em pequenos passos, mas precisa dar um grande salto para fazer com que eles 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 escrever apenas código suficiente para passar um teste de cada vez, ainda assim poderá funcionar. Você teria que ter mais disciplina para não apenas seguir em frente e escrever mais código do que o necessário. Uma vez que você comece esse caminho, você se deixa aberto 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 é dirigido por um teste e talvez no sentido de que não é necessário (ou exercitado) 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 passar em um teste pode levar você a pensar 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 de aprovação. Como eu disse, ter uma boa idéia do que o método deve fazer é muito bom, mas muitas vezes me encontrei encontrando novas possibilidades que não tinha considerado a priori, mas que só ocorreram no processo de escrever testes. Existe o perigo de que você possa sentir falta deles, a menos que você tenha o hábito de pensar que novos testes eu posso escrever que eu ainda não tenho.

    
por 02.04.2012 / 15:08
fonte
3

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 achei muito 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.

    
por 02.04.2012 / 13:37
fonte
2
  • Então você tenta se concentrar em muitas coisas de cada vez.
  • Ao implementar para fazer todos os testes passarem, você não tem uma versão funcional do seu aplicativo. Se você tem que implementar muito, então você não terá uma versão funcional por um longo tempo.
por 02.04.2012 / 12:32
fonte
2

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é você saber quando segui-la e quando você pode quebrá-la (isto é, até saber não tenha que fazer esta pergunta em stackoverflow:)

Tendo feito TDD por quase uma década, posso dizer-lhe que raramente, ou nunca, escrevo muitos testes antes de escrever o código de produção.

    
por 02.04.2012 / 13:24
fonte
1

Você está descrevendo o BDD, em que algumas partes interessadas externas possuem uma especificação executável. Isso pode ser benéfico se houver uma especificação antecipada predeterminada (por exemplo, uma especificação de formato, padrão industrial ou quando o programador não for o especialista em domínio).

A abordagem normal é então gradualmente cobrir mais e mais testes de aceitação, que é o progresso visível para o gerente de projeto e para o cliente.

Você geralmente tem esses testes especificados e executados em uma estrutura de BDD, como Pepino, Fitnesse ou algo assim.

No entanto, isso não é algo que você confunde com seus testes de unidade, que são muito mais próximos dos detalhes da implementação, com muitos casos de limites relacionados à API, problemas de inicialização, etc., strongmente focados no item em teste. / em>, que é um artefato de implementação .

A disciplina de refatoração verde-vermelho traz muitos benefícios, e a única vantagem que você pode esperar digitando-a na frente é empatar.

    
por 02.04.2012 / 12:48
fonte
1

Um teste no momento: a principal vantagem é focar em uma coisa. Pense no design em profundidade: você pode se aprofundar e manter o foco com um feedback rápido. Você pode perder o escopo de todo o problema! Esse é o momento (grande) de refatoração entra em jogo. Sem isso, o TDD não funciona.

Todos os testes: a análise e o design podem revelar mais do escopo do problema. Pense em projeto de largura. Você analisa o problema de mais ângulos e adiciona informações da experiência. É inerentemente mais difícil, mas pode render benefícios interessantes - menos refatoração - se você fizer "o suficiente". Cuidado, é fácil analisar demais e perder completamente a marca!

Geralmente, acho difícil recomendar uma ou outra, porque os fatores são muitos: experiência (especialmente com o mesmo problema), conhecimento e habilidades de domínio, simpatia do código para refatoração, complexidade do problema ...

Eu diria que, se focarmos mais estreitamente em aplicativos de negócios típicos, o TDD, com sua abordagem de erro e avaliação mais rápida, normalmente ganharia em termos de eficácia.

    
por 02.04.2012 / 13:52
fonte
1

Supondo que sua estrutura de teste a suporte, o que eu sugiro é que, em vez de implementar os testes que você deseja identificar, em vez disso, escreva testes descritivos pendentes que serão implementados posteriormente. Por exemplo, se sua API deve fazer foo e bar, mas não biz, basta adicionar o seguinte código (este exemplo está em rspec) para sua suíte de testes e, em seguida, atacá-los um por um. Você descobre seus pensamentos rapidamente e pode resolver todos os seus problemas um por um. Quando todos os testes passarem, você saberá quando resolver todos os problemas que teve durante a sua braindump.

describe "Your API" do

  it "should foo" do
    pending "braindump from 4/2"
  end

  it "should bar" do
    pending "braindump from 4/2"
  end

  it "should not biz" do
    pending "braindump from 4/2"
  end

end
    
por 02.04.2012 / 17:33
fonte