TDD vs. Produtividade

129

No meu projeto atual (um jogo, em C ++), decidi usar o Test Driven Development 100% durante o desenvolvimento.

Em termos de qualidade de código, isso tem sido ótimo. Meu código nunca foi tão bem projetado ou tão livre de bugs. Eu não me arrepio quando vejo código que eu escrevi há um ano no começo do projeto, e eu ganhei um senso muito melhor de como estruturar as coisas, não apenas para ser mais facilmente testável, mas para ser mais simples de implementar e usar .

No entanto ... faz um ano desde que comecei o projeto. Concedido, eu só posso trabalhar nisso no meu tempo livre, mas TDD ainda está me atrasando consideravelmente em comparação com o que eu estou acostumado. Eu li que a velocidade de desenvolvimento mais lenta melhora com o tempo, e eu definitivamente penso em testes muito mais facilmente do que costumava fazer, mas eu estou nisso há um ano e ainda estou trabalhando no ritmo de um caracol.

Cada vez que eu penso no próximo passo que precisa funcionar, eu tenho que parar toda vez e pensar em como eu escreveria um teste para ele, para permitir que eu escrevesse o código real. Eu às vezes fico preso por horas, sabendo exatamente qual código eu quero escrever, mas sem saber como dividi-lo com precisão suficiente para cobri-lo completamente com testes. Outras vezes, eu rapidamente penso em uma dúzia de testes, e passo uma hora escrevendo testes para cobrir um pequeno pedaço de código real que, de outra forma, levaria alguns minutos para escrever.

Ou, depois de terminar o 50º teste para cobrir uma entidade em particular no jogo e todos os aspectos de sua criação e uso, eu olho para minha lista de tarefas e vejo a próxima entidade a ser codificada, e fico horrorizada com a pensou em escrever outros 50 testes semelhantes para implementá-lo.

Chegou ao ponto que, olhando para o progresso do ano passado, estou pensando em abandonar o TDD para "acabar com o maldito projeto". No entanto, desistir da qualidade do código que veio com ela não é algo que eu esteja esperando. Eu tenho medo de que, se eu parar de escrever testes, então eu vou perder o hábito de tornar o código tão modular e testável.

Estou talvez fazendo algo errado para ainda ser tão lento nisso? Existem alternativas que aceleram a produtividade sem perder completamente os benefícios? TAD? Menos cobertura de teste? Como as outras pessoas sobrevivem ao TDD sem matar toda a produtividade e motivação?

    
por Nairou 22.06.2011 / 03:55
fonte

10 respostas

75

Deixe-me começar por agradecer-lhe para compartilhar sua experiência e expressar suas preocupações ... o que eu tenho a dizer não é incomum.

  • Tempo / Produtividade: Escrever testes é mais lento do que não escrever testes. Se você fizer isso, eu concordaria. No entanto, se você executar um esforço paralelo no qual aplique uma abordagem não-TDD, é provável que o tempo que você gasta o código existente de detecção e depuração de detecção de interrupção o colocará na rede negativa. Para mim, o TDD é o mais rápido possível sem comprometer minha confiança no código. Se você encontrar coisas no seu método, que não estão adicionando valor, elimine-as.
  • Número de testes: se você codificar N coisas, precisará testar N coisas. Parafraseando uma das linhas de Kent Beck " Teste somente se você quiser que ele funcione. "
  • Ficando preso por horas: eu também (às vezes, não > 20 minutos antes de parar a linha) .. É apenas o seu código dizendo que o projeto precisa de algum trabalho. Um teste é apenas outro cliente para sua classe SUT. Se um teste está achando difícil usar seu tipo, as chances são tão grandes para seus clientes de produção.
  • Tédio de testes semelhantes: Isso requer um pouco mais de contexto para eu escrever um contra-argumento. Dito isto, pare e pense sobre a semelhança. Você pode conduzir esses testes de alguma forma? É possível escrever testes em um tipo base? Então você só precisa executar o mesmo conjunto de testes contra cada derivação. Ouça seus testes. Seja o tipo certo de preguiça e veja se você consegue descobrir uma maneira de evitar o tédio.
  • Parar para pensar sobre o que você precisa fazer em seguida (o teste / especificação) não é uma coisa ruim. Pelo contrário, é recomendado que você construa "a coisa certa". Normalmente, se não consigo pensar em como testá-lo, geralmente não consigo pensar na implementação. É uma boa ideia apagar as ideias de implementação até chegar lá .. talvez uma solução mais simples seja ofuscada por um design preventivo YAGNI-ish.

E isso me leva à consulta final: como melhorar? Minha (ou An) resposta é Ler, Refletir e Praticar.

por exemplo. Ultimamente, fico de olho em

  • se meu ritmo reflete RG [Ref] RG [Ref] RG [Ref] ou é RRRRGRRef.
  • % de tempo gasto no estado Red / Compile Error.
  • Eu estou preso em um estado vermelho / quebrado?
por 22.06.2011 / 07:09
fonte
31

Você não precisa de 100% de cobertura de teste. Seja pragmático.

    
por 24.06.2011 / 02:48
fonte
22

TDD is still slowing me down considerably

Isso é realmente falso.

Sem o TDD, você gasta algumas semanas escrevendo código que funciona principalmente e passa o próximo ano "testando" e corrigindo muitos (mas não todos) os bugs.

Com o TDD, você gasta um ano escrevendo códigos que realmente funcionam. Então você faz testes finais de integração por algumas semanas.

O tempo decorrido provavelmente será o mesmo. O software TDD será substancialmente melhor qualidade.

    
por 22.06.2011 / 04:01
fonte
20

Or, after finishing the 50th test to cover a particular entity in the game and all aspects of it's creation and usage, I look at my to-do list and see the next entity to be coded, and cringe in horror at the thought of writing another 50 similar tests to get it implemented.

Isso me faz pensar o quanto você está seguindo a etapa "Refactor" do TDD.

Quando todos os seus testes estiverem passando, é hora de refatorar o código e remover a duplicação. Embora as pessoas geralmente lembrem disso, às vezes elas esquecem que também é hora de refatorar seus testes , para remover a duplicação e simplificar as coisas.

Se você tem duas entidades que se mesclam em uma para permitir a reutilização de código, considere a possibilidade de mesclar seus testes também. Você realmente só precisa testar diferenças incrementais em seu código. Se você não está realizando manutenção regularmente nos seus testes, eles podem se tornar rapidamente difíceis de manejar.

Alguns pontos filosóficos sobre o TDD podem ser úteis:

  • Quando você não consegue descobrir como escrever um teste, apesar da extensa experiência em escrever testes, isso é definitivamente um código de cheiro . Seu código é de alguma forma carente de modularidade, o que dificulta a escrita de testes pequenos e simples.
  • Pegar um pouco de código é perfeitamente aceitável ao usar o TDD. Escreva o código desejado, para ter uma ideia de como ele é, exclua o código e comece com os testes.
  • Eu vejo praticar TDD extremamente rigoroso como uma forma de exercício. Quando você começa, definitivamente escreva um teste primeiro a cada vez, e escreva o código mais simples para fazer o teste passar antes de prosseguir. No entanto, isso não é necessário quando você se sentir mais confortável com a prática. Eu não tenho um teste de unidade para cada caminho de código possível que escrevo, mas através da experiência eu posso escolher o que precisa ser testado com um teste de unidade, e o que pode ser coberto pelo teste funcional ou de integração . Se você pratica o TDD de forma rigorosa há um ano, eu imagino que você esteja próximo a esse ponto também.

EDIT: Sobre o tema da filosofia de testes unitários, acho que isso pode ser interessante para você ler: O Caminho do Testivus

E um ponto mais prático, se não necessariamente muito útil:

  • Você menciona o C ++ como sua linguagem de desenvolvimento. Eu pratiquei TDD extensivamente em Java, usando excelentes bibliotecas como JUnit e Mockito. No entanto, eu achei TDD em C ++ para ser muito frustrante devido à falta de bibliotecas (em particular, mocking frameworks) disponíveis. Embora este ponto não o ajude em sua situação atual, espero que você o leve em consideração antes de abandonar completamente o TDD.
por 24.06.2011 / 02:35
fonte
8

Pergunta muito interessante.

O que é importante notar, é que o C ++ não é muito facilmente testável, e o jogo, em geral, também é um candidato muito ruim para o TDD. Você não pode testar se o OpenGL / DirectX chama triângulo vermelho com o driver X e amarelo com o driver Y facilmente. Se o vetor normal do mapa de relevo não for invertido após a transformação do sombreador. Nem você pode testar problemas de recorte sobre versões de driver com diferentes precisões e assim por diante. O comportamento de desenho indefinido devido a chamadas incorretas também pode ser testado apenas com revisão de código precisa e SDK disponível. O som também é um mau candidato. Multithreading, que novamente é muito importante para jogos, é praticamente inútil para o teste unitário. Então é difícil.

Basicamente, jogos são um monte de GUI, som e threads. GUI, mesmo com componentes padrão para os quais você pode enviar WM_, é difícil de testar em unidades.

Então, o que você pode testar, é classes de carregamento de modelo, classes de carregamento de texturas, bibliotecas de matrizes e outras, o que não é muito código e, muitas vezes, não é muito reutilizável, se for apenas seu primeiro projeto. Além disso, eles são compactados em formatos proprietários, portanto, não é muito provável que a entrada de terceiros possa diferir muito, a menos que você libere ferramentas de modificação etc.

Então, novamente, eu não sou guru ou evangelista do TDD, então tome tudo isso com um pouco de sal.

Eu provavelmente escreveria alguns testes para os principais componentes principais (por exemplo, biblioteca de matrizes, biblioteca de imagens). Adicione um monte de abort() em entradas inesperadas em todas as funções. E o mais importante, concentre-se no código resistente / resiliente que não se rompe facilmente.

Em relação a um erro, o uso inteligente de C ++, RAII e um bom design ajuda muito a evitá-los.

Basicamente, você tem muito a fazer apenas para cobrir o básico, se quiser liberar o jogo. Não tenho certeza se o TDD ajudará.

    
por 28.06.2011 / 15:39
fonte
6

Concordo com as outras respostas, mas também quero acrescentar um ponto muito importante: os custos de refatoração !!

Com testes de unidade bem escritos, você pode reescrever com segurança seu código. Primeiro, testes de unidade bem escritos fornecem uma excelente documentação da intenção do seu código. Segundo, quaisquer efeitos colaterais infelizes de sua refatoração serão capturados no conjunto de testes existente. Assim, você garante que as suposições do seu código antigo também são verdadeiras para o novo código.

    
por 23.06.2011 / 14:00
fonte
4

How do other people survive TDD without killing all productivity and motivation?

Isto é completamente diferente das minhas experiências. Você é surpreendentemente inteligente e escreve código sem erros (por ex., Com erros de um) ou você não percebe que seu código tem erros que impedem seu programa de funcionar, e por isso não estão realmente terminados.

TDD é sobre ter a humildade de saber que você (e eu!) cometemos erros.

Para mim, o tempo de gravação de testes unitários é mais do que economizado em tempo reduzido de depuração para projetos que são feitos usando o TDD desde o início.

Se você não cometer erros, talvez o TDD não seja tão importante para você como é para mim!

    
por 24.06.2011 / 18:21
fonte
3

Eu tenho apenas algumas observações:

  1. Parece que você está tentando testar tudo . Você provavelmente não deveria, apenas os casos de alto risco e borda de um determinado código / método. Tenho certeza de que a regra 80/20 se aplica aqui: você gasta 80% de testes de escrita para os últimos 20% do seu código ou casos que não são cobertos.

  2. Priorizar. Entre no desenvolvimento ágil de software e faça uma lista do que você realmente precisa fazer para lançar em um mês. Então solte, assim mesmo. Isso fará com que você pense sobre a prioridade dos recursos. Sim, seria legal se seu personagem pudesse fazer um backflip, mas ele tem valor comercial ?

O TDD é bom, mas somente se você não pretende atingir 100% de cobertura de teste, e não é porque ele impede que você produza valor comercial real (ou seja, recursos, coisas que adicionam algo ao seu jogo).

    
por 28.06.2011 / 17:06
fonte
1

Sim, escrever testes e código pode demorar mais do que apenas escrever código - mas escrever código e testes de unidade associados (usando TDD) é muito mais previsível do que escrever código e, em seguida, depurá-lo.

A depuração é quase eliminada quando se utiliza o TDD - o que torna todo o processo de desenvolvimento muito mais previsível e, no final, indiscutivelmente mais rápido.

Refatoração constante - é impossível fazer qualquer refatoração séria sem um abrangente conjunto de testes unitários. A maneira mais eficiente de construir essa rede de segurança baseada em testes de unidade é durante o TDD. Código bem refatorado melhora significativamente a produtividade geral do designer / equipe que mantém o código.

    
por 28.06.2011 / 05:24
fonte
0

Considere reduzir o alcance do seu jogo e obtê-lo onde alguém possa tocá-lo ou liberá-lo. Manter seus padrões de teste sem ter que esperar muito tempo para liberar seu jogo pode ser um meio-termo para mantê-lo motivado. O feedback de seus usuários pode fornecer benefícios a longo prazo e seu teste permite que você se sinta confortável com as adições e alterações.

    
por 08.07.2011 / 02:17
fonte