Quais são as desvantagens da programação de teste primeiro?

47

É toda a raiva hoje em dia. "Todo mundo" recomenda. Isso por si só me deixa desconfiado.

Quais são algumas desvantagens que você encontrou ao fazer o desenvolvimento de teste primeiro (orientado a teste)? Estou procurando experiências pessoais de profissionais experientes - posso ler as reflexões hipotéticas de uma centena de aspirantes a outros lugares da Internet.

Não pergunto porque estou querendo odiar o TDD, mas porque é meu trabalho melhorar o processo de desenvolvimento de software e quanto mais aprendermos sobre os problemas que as pessoas encontram, maior a chance de melhorar o processo.

Alex Feinman
fonte

Respostas:

41

Há muito poucos, mas as vantagens longe superam as desvantagens.

Há uma curva de aprendizado acentuada.

Muitos desenvolvedores parecem esperar que possam ser eficientes com a programação de teste primeiro desde o primeiro dia. Infelizmente, leva muito tempo para ganhar experiência e programar na mesma velocidade de antes. Você não pode contornar isso.

Para ser mais específico, é muito fácil errar. Você pode muito facilmente (com muito boas intenções) escrever vários testes difíceis de manter ou testar coisas erradas. É difícil dar exemplos aqui - esse tipo de problema simplesmente exige experiência para ser resolvido. Você precisa ter uma boa idéia de separar as preocupações e projetar a testabilidade. Meu melhor conselho aqui seria fazer a programação em pares com alguém que conhece muito bem o TDD.

Você faz mais codificação antecipadamente.

O teste primeiro significa que você não pode pular testes (o que é bom) e significa que você acabará escrevendo mais código com antecedência. Isso significa mais tempo. Novamente, você não pode contornar isso. Você é recompensado com um código que é mais fácil de manter, estender e geralmente menos bugs, mas leva tempo.

Pode ser uma venda difícil para os gerentes.

Os gerentes de software geralmente se preocupam apenas com os cronogramas. Se você mudar para a programação do primeiro teste e de repente levar duas semanas para concluir um recurso em vez de um, eles não vão gostar. Esta é definitivamente uma batalha que vale a pena travar e muitos gerentes são esclarecidos o suficiente para consegui-la, mas pode ser uma venda difícil.

Pode ser uma venda difícil para outros desenvolvedores.

Como existe uma curva de aprendizado acentuada, nem todos os desenvolvedores gostam de programação de teste primeiro. Na verdade, eu acho que a maioria dos desenvolvedores não gosta disso no começo. Você pode fazer coisas como programação em pares para ajudá-los a se atualizar, mas pode ser uma venda difícil.

No final, as vantagens superam as desvantagens, mas não ajuda se você apenas ignorar as desvantagens. Saber o que você está lidando desde o início ajuda a negociar algumas, se não todas, as desvantagens.

Jaco Pretorius
fonte
Essas são boas respostas, mas poderia ser mais específico sobre o número 1? Estou especialmente interessado em saber como / se você conseguiu recuperar sua velocidade de programação - o que você aprendeu que não sabia quando começou a usar o TDD?
precisa saber é o seguinte
Atualizado para dar alguns esclarecimentos
Jaco Pretorius
7
Se você estiver testando agora, o tempo total gasto em desenvolvimento não deve mudar significativamente. Parece que as coisas estão demorando mais porque você está expondo o tempo necessário para escrever e manter testes de unidade.
ChrisF
1
@JeffO, você está familiarizado com o "Eu vou me escrever uma minivan!" escola de codificação?
Alex Feinman
1
@tvanfosson - porque eles estão tentando mudar duas coisas ao mesmo tempo - começando o teste e também o TDD - o que pode ser problemático. Ele também aumenta as estimativas de tempo - de maneira correta - para que gerentes e clientes apenas vejam o aumento inicial, não que o tempo total seja realmente conhecido (pela primeira vez) e possa até ser menor. Se eles estiverem fazendo alguns testes, esse aumento será menor.
ChrisF
35

O teste primeiro pressupõe que você esteja escrevendo um código que seja:

  • testável de maneira unitária
  • que o que você está desenvolvendo tem uma abordagem óbvia e não exigirá prototipagem ou experimentação extensiva
  • que você não precisará refatorar demais ou que tenha tempo para reescrever centenas ou milhares de casos de teste repetidamente
  • nada está selado
  • tudo é modular
  • tudo é injetável ou ridicularizado
  • que sua organização valorize suficientemente baixos defeitos para justificar o coletor de recursos
  • que há algo de útil para testar em um nível de teste unitário

Se o seu projeto não atender a esses requisitos, você terá dificuldades. Os promotores do TDD não têm boas respostas a esse outro para sugerir que você redesenhe seu produto para se enquadrar melhor nessas linhas. Há situações em que isso é impossível ou indesejável.

Na prática, também pode haver um grande problema para as pessoas que pensam que os testes do primeiro teste realmente provam algo sobre a função correta do programa. Em muitos casos, isso não é verdade, mas mesmo nos casos em que é verdade, está longe de ser uma imagem completa da correção. As pessoas veem centenas de testes aprovados e assumem que é seguro testar menos, pois antes do TDD eles apenas faziam algumas centenas de casos de teste. Na minha experiência, TDD significa que você precisa ter ainda mais testes de integração, pois os desenvolvedores também terão a falsa segurança e a dor de alterar todos os testes para fazer um grande redator pode levar os desenvolvedores a fazer soluções interessantes.

Exemplos:

Meu melhor exemplo pessoal é ao escrever um código de segurança para o asp.net. Se eles devem ser executados em um ambiente hostil a partir da configuração da máquina, são armazenados, assinados e lacrados e, como estão sendo executados contra objetos divinos do IIS, é muito difícil zombar muito corretamente. Adicione algumas restrições ao uso do desempenho e da memória e você perderá rapidamente a flexibilidade de usar objetos de espaço reservado nas áreas restantes.

Qualquer tipo de microcontrolador ou outro código de ambiente com poucos recursos pode não ser possível para criar um design verdadeiramente de estilo OO, pois as abstrações não são otimizadas e você tem limites de recursos baixos. O mesmo pode ser dito para rotinas de alto desempenho em muitos casos também.

Conta
fonte
Você pode dar alguns contra-exemplos? Quando eu não estaria escrevendo algo testável de maneira unitária? Por que eu não estaria escrevendo um código que pode ser ridicularizado ou injetável (além do código herdado, que é um tópico em si)?
Alex Feinman
editado para adicionar seção de exemplos
Bill
4
Acordado. O trabalho com TDD parece depender de um conjunto de suposições sobre as máquinas com as quais você está trabalhando; parece não ser verdade para cerca de 50% dos meus projetos.
Paul Nathan
Eu concordo totalmente ... Grande resposta
Khelben
2
É como qualquer coisa neste jogo - apropriado para muitas situações, inapropriado para outros. Cuidado com qualquer pessoa que defenda um caminho verdadeiro em qualquer área do desenvolvimento de software.
Alan B
25

A maior desvantagem que já vi não é com o próprio TDD, mas com os profissionais. Eles adotam uma abordagem dogmática e fanática, onde tudo deve ser testado . Às vezes (muitas vezes), isso não é necessário. Além disso, pode não ser prático (ou seja, a introdução de uma organização no TDD.)

Um bom engenheiro encontra compromissos e aplica o equilíbrio certo de quando / onde / como aplicar o teste primeiro. Além disso, se você estiver constantemente gastando muito mais tempo desenvolvendo testes em vez de código real (por um fator de 2 a 3 ou mais), estará com problemas.

Em outras palavras, seja pragmático e razoável com o TDD (ou qualquer outra coisa no desenvolvimento de software).

luis.espinal
fonte
Talvez seja aí que a "nova" definição de Código Legado de Michael Feathers (isto é, "Código sem Testes") vem?
Phill W.
Essa definição não funcionaria para mim :) Para mim, qualquer código executado em produção e sujeito a alterações é um código herdado, independentemente do código ou da qualidade do teste. Normalmente, associamos "código herdado" a "código incorreto" ou "código obsoleto" quando, na realidade, código incorreto e código obsoleto já estão presentes no código em desenvolvimento que ainda não foi utilizado na produção. Nosso objetivo deve ser que nosso código seja legado desde o início e tenha tanta qualidade e utilidade que permaneça em uso por anos e décadas.
Luis.espinal
6

Comecei a fazer TDD no início de agosto de 2009 e convenci toda a minha empresa a mudar para ele em setembro / outubro de 2009. Atualmente, toda a equipe de desenvolvedores é totalmente convertida e o comprometimento de código não testado no repositório é considerado uma coisa ruim e usada. Tem funcionado muito bem para nós e não consigo imaginar voltar à codificação de cowboys.

No entanto, existem dois problemas que são bastante visíveis.

O conjunto de testes deve ser mantido

Quando você leva a sério o TDD, você acaba escrevendo muitos testes. Além disso, leva algum tempo e experiência para perceber qual é a granularidade certa de testes (exagerar é quase tão ruim quanto subexpor). Esses testes também são de código e são suscetíveis ao bitrot. Isso significa que você deve mantê-las como todo o resto: atualize-a quando atualizar as bibliotecas das quais elas dependem, refatorando de tempos em tempos ... Quando você faz grandes alterações no seu código, muitos testes ficam subitamente desatualizados ou mesmo errado. Se você tiver sorte, pode simplesmente excluí-los, mas muitas vezes acabará extraindo os bits úteis e adaptando-os à nova arquitetura.

Abstrações de teste vazam de tempos em tempos

Estamos usando o Django, que possui uma ótima estrutura de teste. No entanto, às vezes, faz suposições ligeiramente divergentes da realidade. Por exemplo, algum middleware pode interromper os testes. Ou, alguns testes fazem suposições sobre um back-end de cache. Além disso, se você estiver usando um banco de dados "real" (não o SQLite3), a preparação do banco de dados para os testes levará muito tempo. Claro, você pode (e deve) usar o SQLite3 e um banco de dados na memória para testes que você faz localmente, mas algum código se comportará de maneira diferente, dependendo do banco de dados usado. É necessário configurar um servidor de integração contínua executado em uma configuração realista.

(Algumas pessoas dirão que você deve zombar de todas as coisas, como o banco de dados, ou que seus testes não são "puros", mas isso é apenas ideologia. Se você cometer erros no seu código de zombaria (e acredite, você o fará), seu testinguite não terá valor.)

Isso dito, os problemas que descrevi começam a ser notados apenas quando você está bastante avançado com o TDD ... Quando você está apenas começando com o TDD (ou trabalhando em projetos menores), a refatoração de teste não será um problema.

Ryszard Szopa
fonte
3
+1. "tem que ser mantido": isso é muito menos problemático ao testar código reutilizável, pois sua interface e comportamento normalmente precisam ser estáveis. Por esse motivo, normalmente faço TDD apenas para nossa biblioteca reutilizável.
Dimitri C.
4

Para mim, há um profundo problema psicológico nos testes sempre que tento aplicá-los extensivamente, como no TDD: se eles estiverem lá, eu codifico com desleixo porque confio que os testes detectarão qualquer problema. Mas, se não houver testes para fornecer uma rede de segurança, codifico com cuidado e o resultado é invariavelmente melhor do que com os testes.

Talvez seja só eu. Mas também li em algum lugar que carros com todos os tipos de alarmes e assobios de segurança tendem a bater mais (porque os motoristas sabem que os recursos de segurança existem), então talvez isso seja algo a ser reconhecido; TDD pode ser incompatível com alguns indivíduos.

Joonas Pulakka
fonte
Isso me parece estranho, já que escrever código testável geralmente me leva a desacelerar e pensar mais sobre o que estou codificando. Atualmente, fico um pouco nervoso com a codificação sem testes.
Matt H
1
Isso apenas mostra que pessoas diferentes realmente reagem de maneira diferente. Não estou criticando o TDD - obviamente, algumas pessoas o acham útil - mas o fato é que não é para todos.
Joonas Pulakka 15/10/10
2
Eu concordo 100%. Escrevo código melhor e mais rápido sem testes automatizados. Claro que seria absurdo não testar, apenas acho que a automação é uma má escolha (pelo menos para mim). Considero o teste manual mais rápido do que manter um conjunto de testes e mais seguro - mas também sou um desenvolvedor experiente, por isso sou muito bom em saber o que testar e onde e por que, para que minhas adições e re-fatores de código sejam livre de regressão.
21412 Ben Lee
1
Embora eu deva salientar que a equipe com a qual trabalho e os projetos são pequenos o suficiente para ter um bom senso de toda a arquitetura - em uma equipe grande ou em um projeto muito grande, pude ver os testes automatizados como mais úteis, porque então, um desenvolvedor único não seria necessariamente capaz de cheirar onde deveria estar testando para evitar regressões.
21712 Ben Lee
Você está deixando de fora a etapa de refatoração?
Rjnilsson
2

Uma situação em que o teste primeiro realmente me atrapalha é quando eu quero experimentar rapidamente alguma idéia e ver se ela pode funcionar antes de escrever uma implementação adequada.

Minha abordagem é normalmente:

  1. Implemente algo que seja executado (prova de conceito).
  2. Se funcionar, consolide adicionando testes, melhorando o design, refatorando.

Às vezes, não chego ao passo 2.

Nesse caso, o uso do TDD resultou em mais desvantagens do que vantagens para mim:

  • Escrever testes durante a implementação da prova de conceito apenas me deixa mais lento e interrompe meu fluxo de pensamentos: quero entender uma idéia e não quero perder tempo testando detalhes da minha primeira implementação aproximada.
  • Pode levar mais tempo para descobrir se minha ideia vale alguma coisa ou não.
  • Se a idéia for inútil, tenho que jogar fora meu código e meus testes de unidade bem escritos.

Portanto, quando tenho que explorar algumas idéias novas, não uso o TDD e apenas apresento testes de unidade quando sinto que o novo código está chegando a algum lugar.

Giorgio
fonte
1
Parece que você está confundindo código de protótipo com código utilizável. Código de protótipo é código de teste . Ele não precisa ser testado e você não deve criar testes que sejam executados contra ele. A etapa que está faltando é entre 1. e 2: você diz "consolidar escrevendo testes". O problema é que você não tem algo para consolidar, mas algo para escrever. Planeje reescrever o código do protótipo, não planeje reutilizá-lo. Reutilizá-lo deixa muito espaço para compromisso. A reescrita formaliza a divisão entre a fase de exploração e a fase "código de qualidade".
Utnapistim 6/10/2014
3
@utnapistim: Não estou confundindo código de protótipo com código utilizável, mas os fanáticos do TDD os confundem e sugerem que você também deva usar o TDD para o código do protótipo. Ou melhor, eles assumem que não há código de protótipo. Além disso, concordo que muitas vezes você precisa reescrever quando passa do protótipo para a implementação real. Às vezes, você pode reutilizar partes do código do protótipo, mas deve estar pronto para reescrever. Você realmente tem que decidir caso a caso.
Giorgio
3
@utnapistim: Veja também a resposta de luis.espinal: "A maior desvantagem que já vi não é com o próprio TDD, mas com profissionais. Eles adotam uma abordagem dogmática e fanática, onde tudo deve ser testado".
Giorgio
1

Desvantagens ou custos do TDD

Nota: Há uma variedade de tipos diferentes de TDD. Independentemente da unidade, BDD, ATDD ou outras variantes, muitas das dificuldades permanecem

Efeitos colaterais

Seja zombaria, acessórios ou testes funcionais, as dependências de estados ou sistemas externos costumam ser a fonte de maior complexidade nos testes, confusão em como testar e o maior risco de errar. Alguns problemas que eu já vi:

  • Zombando: esqueça de afirmar a ordem das chamadas
  • Zombando: o mock não corresponde à chamada ou resposta real
  • Dispositivo elétrico: o teste depende de dados irreais, ocultando outros problemas
  • Dispositivo elétrico: teste um estado impossível na produção
  • Funcional: falhas de construção falsas devido à indisponibilidade temporária do sistema dependente
  • Funcional: a velocidade do teste é muito lenta

Você terá que mudar sua abordagem de codificação, para alguns será uma mudança drástica.

Pessoas diferentes codificam de maneiras totalmente diferentes. No TDD, você precisa começar com um teste que afirme um comportamento específico e depois implementá-lo para que o teste seja aprovado. Eu já vi e era um programador cuja programação não era propícia ao TDD. Levei cerca de 2 meses quando comecei a me acostumar a mudar minha abordagem de desenvolvimento.

Leva tempo para entender o que você se importa com o teste e o que você não se importa com o teste.

Toda equipe deve tomar uma decisão explícita sobre onde deseja definir a linha nos testes. O que eles valorizam e querem testar, e o que não valorizam. Geralmente, é um processo doloroso aprender a escrever bons testes e o que você realmente se importa com os testes. Enquanto isso, o código continuará em um estado de fluxo até que haja consistência no estilo e na abordagem.

Teste unitário específico: grandes refatores

Um refator grande ou fundamental de uma base de código significativa com dezenas de milhares de testes de unidade gerará um custo enorme para atualizar todos os testes. Isso geralmente se manifesta em resposta ao fazer um refator, mesmo que seja a coisa correta a ser feita simplesmente pelo custo associado a isso.

dietbuddha
fonte
0

Minha analogia são barreiras em uma pista Scalextric. Se você os colocar, ficará muito menos cauteloso.

As pessoas também recebem um pouco de cadetismo espacial sobre seus testes - porque eles correm bem, acreditam que o código foi totalmente testado, enquanto é apenas o começo do processo de teste.

Na minha opinião, TDD é um trampolim para o BDD. Uma série de testes executados realmente não ajuda no suporte aos desenvolvedores sem saber o que os testes fazem. Com o BDD, a saída do teste é em inglês, que documenta o teste e, assim, constrói a compreensão do sistema.

Robbie Dee
fonte
-1

Os benefícios do TDD são que obriga a proteger seu código contra pessoas que não o entendem. Sim, isso geralmente inclui você mesmo. Mas, o que acontece quando o código não vale a pena guardar? Há muito código que nem deveria estar lá em primeiro lugar! Portanto, o problema com o TDD é quando se trata de desenvolvedores que escrevem códigos incorretos. O TDD provavelmente não os ajudará a escrever um bom código, é muito mais provável que eles escrevam testes horríveis também. Assim, no caso deles, o TDD adicionará apenas à bagunça; testes mal escritos e / ou redundantes não são mais divertidos do que outras formas de código incorreto.

Johan
fonte
1
Se você mesmo não entende seu próprio código, como um punhado dentre bilhões de possíveis casos de teste pode se proteger contra o código estar errado?
Michael Shaw
2
Porque você entendeu quando o escreveu, mas esqueceu ao longo do caminho?
Johan
O +1 TDD não protege contra um desenvolvedor que não entendeu um requisito comercial. Este é o lugar onde BDD vem em ...
Robbie Dee