Por que o ágil tem tudo a ver com desenvolvimento orientado a teste (TDD) e não teste orientado a desenvolvimento (DDT)?

74

Portanto, sou novo no desenvolvimento ágil, mas não orientado a testes . Meus professores na faculdade eram todos sobre a idéia de testes, depois código e testes. Não sei se entendi o porquê. Da minha perspectiva, é muito custo inicial que provavelmente será alterado à medida que seu código evoluir.

É assim que imagino o TDD e por que isso me confunde. Se eu fosse construir uma casa como empreiteiro de TDD.

  1. Dê-me todas as suas especificações (histórias).

  2. Obtenha aprovação das especificações.

  3. Divida todas as especificações em inspeção que acho que vou precisar (veja no futuro).

  4. Ligue para um inspetor para examinar esses pontos e me diga agora que estou reprovando na inspeção (gee, obrigado).

  5. Comece a construir a casa.

  6. Ligue para o inspetor de volta diariamente (passando em 2/100).

  7. Oh, dispara, houve um problema com meu entendimento e agora preciso adicionar mais 9 inspeções e alterar 27 delas.

  8. Inspetor de chamada passando em 1/109.

  9. Droga. Por que o inspetor não gosta deste? Atualizei o nome do método ...

  10. Construa um pouco mais.

  11. UGGGGHHHH MAIS ALTERAÇÕES, deixe-me atualizar o maldito inspetor. Oh, eu estou falhando, não s ** t.

  12. Eu já terminei?

Tudo bem, isso pode ser estranho, mas não vejo como devo conhecer todos os meus métodos e como as coisas vão funcionar até que meu código esteja lá. Em 99% das vezes, preciso voltar e atualizar um teste de unidade de qualquer maneira e adicionar mais conforme for. Parece apenas ao contrário.

O que parece mais apropriado é o DDT ou o teste orientado para o desenvolvimento, algo que a comunidade quase esqueceu.

Pelo meu entendimento, o DDT para uma casa seria semelhante a:

  1. Dê-me todas as suas especificações (histórias).

  2. Obtenha aprovação das especificações e divida-as.

  3. Inicie uma unidade (a base).

  4. Faça anotações (comentários) de alguma lógica complicada.

  5. No final, antes de iniciar a próxima unidade, faça a inspeção (crie um teste).

  6. Corrija qualquer problema encontrado e inspecione novamente.

  7. Aprovada esta unidade, passe para a próxima.

Se todos somos honestos, isso não soa mais humano e centrado no desenvolvedor e nos negócios? Parece que as alterações podem ser feitas mais rapidamente e sem a sobrecarga que o TDD parece criar.

nerdista
fonte
60
A desconexão gritante para mim é a ideia de que você pode ter mais de 100 testes com falha antes de escrever o código que começa a passar por eles. Como @Telastyn diz, não deve haver mais do que alguns testes com falha por vez. "Testes e depois código e teste" não significa escrever todos os testes do aplicativo antes de escrever qualquer código.
Eric Rei
33
As metáforas são um antipadrão! As pessoas passarão mais tempo explicando por que sua metáfora não se encaixa no assunto, em vez de abordar sua preocupação real. Nesse caso, sua preocupação real parece ser a de que você precisa escrever todos os testes antecipadamente.
precisa saber é o seguinte
21
Como observação lateral, o TDD é comumente usado em projetos ágeis, mas acho importante distinguir entre gerenciamento ágil de projetos e práticas de desenvolvimento ágil. Não existe uma relação muito forte entre os dois. Você pode usar as práticas de desenvolvimento mais ágeis, independentemente da metodologia do projeto e vice-versa. Muitas vezes esquece-se que o ágil, na essência, é realmente adaptar sua abordagem conforme necessário para obter o melhor resultado, e não uma lista prescritiva de coisas que você deve fazer. Em outras palavras, Agile! = Scrum + TDD.
JimmyJames
35
Para o registro, comparar o desenvolvimento de software com a construção de uma casa é inútil. Os estudos foram feitos. Os resultados estão chegando. A resposta é clara. Você simplesmente não constrói software da mesma maneira que constrói uma casa. O. Dois. Estão. Diferente.
Riwalk # 03/16
14
Agile é sobre loops de feedback rápidos, o TDD é apenas um tipo específico de loop de feedback
jk.

Respostas:

83

Um dos benefícios de uma abordagem TDD só é percebido quando você também faz projetos emergentes.

Portanto, em sua primeira analogia, você não escreveria 100 testes, pois não há como saber como será o software.

Você escreve um teste. Você corre. Falha. Você escreve a menor unidade de código para fazer seu teste passar. Então você executa seu teste novamente. Passa.

Agora escreva o próximo teste, repetindo o processo acima.

Isso pode parecer uma abordagem inútil no início, quando é óbvio o que seu código deve fazer, mas o melhor dessa abordagem é que sua cobertura de teste é sempre alta e o design do código é mais limpo dessa maneira.

Como método, ele anda de mãos dadas com a programação de pares; um par grava o teste, o próximo grava o código para fazê-lo passar, depois grava o próximo teste e assim por diante.

Eu até uso essa abordagem ao escrever uma nova classe; o primeiro teste é uma chamada para iniciar o construtor da classe. Mas ainda não escrevi a classe, então ela falha. Em seguida, escrevo a classe simples e vazia e meu primeiro teste passa.

Quando você entra na mentalidade, é muito difícil não estar nela e codificar da maneira "antiquada".

Eu recomendo trabalhar em um bom ambiente Agile para aprender isso ou ler alguns bons livros Agile (Clean Code e Clean Coder são ambos muito bons) para entender melhor.

Phil Riley
fonte
46
O termo "Design emergente" faz parecer que você pode desenvolver um design de software fazendo testes passarem. Você não pode.
Robert Harvey
28
@nerdlyist: Mas não se esqueça do design. Você ainda precisa projetar software; um bom design não surge apenas naturalmente do refator vermelho-verde. O TDD incentiva o bom design, mas não o cria.
Robert Harvey
19
@RobertHarvey, eu discordo completamente do seu " O termo" Design emergente "faz parecer que você pode desenvolver um design de software fazendo testes passarem. Você não pode " afirmação. Tendo feito exatamente isso em várias ocasiões, posso garantir que isso pode ser feito.
David Arno
12
@DavidArno: Você começa com uma especificação de requisitos em mãos? Como você traduz essa especificação em uma arquitetura sensata? Você o reinventa toda vez com testes ou tem em mente os princípios arquiteturais anteriores? Ninguém faz isso às cegas; não há nada mágico em escrever seus testes primeiro, a não ser que o force a tornar seu código mais testável.
Robert Harvey
45
@DavidArno Acho que sua objeção decorre de não dar a si mesmo crédito suficiente como desenvolvedor / arquiteto experiente. Como você já sabe como é o módulo X intuitivamente , seus testes direcionarão seu desenvolvimento em uma boa direção. Um desenvolvedor inexperiente, com pouca ou nenhuma noção intuitiva da aparência de um módulo antes de escrever os testes, criará um módulo de baixa qualidade e bem testado.
Svidgen
86

Software não é uma casa. A intuição é boa, mas entenda que nem sempre é correta.

Divida todas as especificações em inspeção que acho que vou precisar (veja no futuro).

Isso não é exato. No TDD, você está descrevendo como deseja usar o código. As especificações dizem "Deve haver uma casa, com uma maneira de entrar nela". O teste então diz "Ei, eu quero ter uma porta da frente, com uma maçaneta". Isso é muito menos visível no futuro do que começar com a implementação da construção de um batente de porta, botão, trava de chave etc. (ou assim continua o argumento).

Ligue para o inspetor de volta diariamente (passando em 2/100).

Isto não está correto. Você não está escrevendo testes para a casa. Você está escrevendo testes para o batente da porta da frente e depois tornando-os verdes. Em seguida, testa se a porta é sólida, tornando-a verde. Você deve ter cerca de uma dúzia de testes, no máximo, quebrados a qualquer momento. Geralmente, é mais perto das duas para as quatro.

Além disso, a analogia "chamar o inspetor" implica que leva uma certa quantidade de tempo para que a pessoa saia e faça seu trabalho. A execução de testes de unidade para uma iteração TDD deve levar literalmente alguns segundos.

Parece que as alterações podem ser feitas mais rapidamente e sem o TDD aéreo parecer criar.

A sobrecarga é menor do que você parece estar implicando. Alguns segundos para executar os testes, talvez meia dúzia de vezes, são irrelevantes para o tempo geral de desenvolvimento.

O problema do dev primeiro é que, às vezes, quando você faz o teste, descobre que há um grande problema. Como você coloca a cama ao lado do vaso sanitário e ninguém quer morar lá tipo de problema. Coisas que levam mais tempo para consertar do que qualquer tipo de sobrecarga de TDD. Coisas que o TDD teria percebido, já que o TDD obriga a usar seu código desde o início e pensar em como fazer interface com ele.

Meus professores na faculdade eram todos sobre a idéia de testes, depois código e teste.

Tudo isso dito, TDD não é tão onipresente. Muitos lugares ainda desenvolvem primeiro e muitos dos supostos benefícios do TDD são exagerados. O importante é que você faça os testes e os faça bons. A ordem em que você faz o trabalho é menos importante.

Telastyn
fonte
18
+1 apenas no último parágrafo. Os exemplos de sua casa realmente destacam o quão tolo o "religiosoTDD" pode ser.
Robert Harvey
23
"O importante é que você faça os testes e os torne bons. A ordem para realizar o trabalho é menos importante. " Sou um grande fã do TDD (Test-Driven-Development) e defendo isso com frequência. No entanto, escrever código é confuso. Para problemas mais difíceis e menos bem definidos, normalmente preciso escrever primeiro um código antes de entender quais são os requisitos. Então eu posso escrever os requisitos e depois escrever os testes.
Trevor Boyd Smith
1
@TrevorBoydSmith, você não estaria escrevendo a documentação naquele momento, nem os requisitos.
Nerdlyist
8
Como o TDD coloca na perspectiva do usuário do código, [no melhor dos casos] ele promove melhores interfaces. Por exemplo, quando eu ligo open_door, não preciso passar uma bandeira toilet_flushed; portanto, não escrevo meu teste dessa maneira e, portanto, meu código não possui essas "arestas". O código escrito inteiramente sem considerar suficientemente como é chamado geralmente possui interfaces ou suposições / condições prévias estranhas.
brian_o
3
@TrevorBoydSmith para colocar um nome nele, ouvi dizer que "Spike & Stabilize" e sim, é uma técnica útil para descobrir como fazer algo que você ainda não sabe fazer.
precisa
13

As semelhanças entre criar uma coisa física e escrever um software são mínimas.

Dito isto, há uma grande distinção que vale a pena destacar:

Há uma diferença entre "criar um teste" e "executar um teste".

No exemplo da construção de uma casa, os requisitos e testes que precedem o buildout física. E partes da suíte de testes são executadas continuamente - por vários agentes! Quando o construtor pega um 2x4, ele imediatamente e automaticamente "testa" a unidade contra sua noção de "como é um som 2x4". Ele não cria requisitos depois de pegar; ele executa verificações pré-existentes.

Da mesma forma, para uma parede montada, uma caixa elétrica, o encanamento, etc - os testes / requisitos já existem; eles são executados de forma implícita e automática pelos olhos treinados de todos no trabalho continuamente . Outro conjunto de testes preexistentes é executado quando o inspetor visita. Se as unidades não foram construídas para passar no teste preexistente , elas falham.

E da mesma forma para a estrutura como um todo. Os planos pré-existem o buildout. A cada passo do caminho, os engenheiros estão trabalhando em direção a um conjunto preexistente de condições em que a estrutura será testada quando a construção estiver concluída.

Dito isto, nem todo projeto físico precisa ser precedido por um enorme conjunto de testes. Se você for à sua oficina e começar a juntar madeira para uma caixa de brinquedos ou algo assim, deixando sua intuição e criatividade guiá-lo, isso não é um trabalho rigoroso em madeira TDD. Nesse caso, você basicamente conta com as leis físicas do meio e suas expectativas aproximadas para validar o trabalho (ou seja, "se ele compilar e funcionar, será bom!").

E tudo bem. Nem tudo precisa ser precedido por testes rigorosos.

tl; dr

Mesmo que construção! = Software de escrita: a construção opera de maneira orientada a testes. As condições de "passar" para cada unidade de fazer preceder a sua buildout.

Não confunda "executando testes" com "escrevendo testes".

Nem tudo precisa ser TDD.

svidgen
fonte
Eu recebo código! = Físico é uma analogia. Mas isso realmente esclarece um pouco mais para mim. Como meu outro comentário ressalta, estou me perguntando se isso criar o teste primeiro dificulta o desejo de voltar e fazer alterações maiores agora?
Nerdlyist
@nerdlyist De certa forma, sim. Mas, se seus testes estão verificando as coisas "certas", é um "obstáculo" que você deseja .
Svidgen
4
Voltando à analogia da casa: se seu cliente repentinamente quiser trocar em um estilo diferente de porta, e não tiver as mesmas dimensões, os requisitos em torno da porta e da abertura serão alterados, e o trabalho precisará alterar esses "testes" "deve refletir o trabalho necessário para fazer a implementação mudar. Mas existem muitos testes que não mudam: a porta deve abrir e fechar, trancar e ser P% hermética na abertura quando fechada, etc ... Muitos testes que não mudam isso "impedem" mudanças físicas apropriadamente .
Svidgen #
1
Se eu pudesse marcar novamente com +1, seria útil considerar esse último bit. Nem tudo precisa de um teste. Além disso, relê-lo me fez pensar que talvez eu esteja fundindo testes de unidade TDD com testes de integração e regressão. Os trabalhadores testam automaticamente as unidades, o inspetor é a integração e o teste de regressão é realizado ao fazer alterações maiores.
Nerdlyist
1
@nerdlyist Os testes de integração e regressão também podem ser feitos primeiro. Fazer isso não se encaixa perfeitamente no ciclo de desenvolvimento dos "dogmáticos de TDD", mas o resultado é basicamente o mesmo: você acaba com um código testável, com um mínimo de código estranho e com testes que realmente validam o que afirmam. Testar depois de escrever o código pode tornar um pouco mais difícil realizar uma ou todas essas coisas. Não é impossível, mas potencialmente mais difícil.
Svidgen
11

Você caiu na armadilha de acreditar na idéia absurda de que escrever software é análogo a construir uma casa. Não é. É mais análogo à criação dos desenhos dos arquitetos e dos cálculos dos engenheiros estruturais.

Agora, com casas reais, o arquiteto cria esses planos com antecedência. Em seguida, você chama os construtores, que começam a construir, enfrentam problemas, precisam alterar as coisas, obter o controle de construção, quem quer mudanças etc. Então, no final, o arquiteto volta e cobra mais por atualizar seus desenhos para refletir o que aconteceu. Essa é uma maneira ruim de fazer as coisas, mas as casas levam muito tempo para serem construídas e são caras, por isso é a única maneira prática.

As coisas são melhores para a engenharia de software. O equivalente à construção da casa é fazer com que o compilador transforme seu código em uma unidade compilada de algum tipo (biblioteca, aplicativo, aplicativo da web etc.). É muito rápido e barato fazer isso centenas de vezes por dia. Como resultado, não faz sentido reconstruir a casa repetidamente à medida que você adiciona recursos e apenas chamar o inspetor (QA) no final para testá-lo. Em vez disso, se você automatizar essas verificações, o inspetor poderá inspecionar tudo sempre que reconstruir.

Se você segue o TDD estrito ou uma abordagem mais orientada para o teste de escrever algum código, então alguns testes, realmente não importa. Eu prefiro a primeira abordagem, outras a segunda. Escolha o que mais lhe convier. O importante é que você crie essas verificações à medida que avança. Eles ajudam mais tarde quando você deseja alterar o código, avisando sobre falhas na funcionalidade em outros lugares quando você altera alguma coisa.

David Arno
fonte
1
Vejo que você já lidou com o controle de construção, então ... :) #
314 Jules
5
"Essa é uma maneira péssima de fazer as coisas, mas as casas levam muito tempo para serem construídas e são caras, por isso é a única maneira prática.": É verdade que no software você pode reconstruir muito rapidamente, mas se cometer um erro completamente errado. decisão no início, você pode precisar de uma grande reescrita mais tarde. O design incremental nem sempre pega esse tipo de problema: você se move em pequenos passos até se encontrar em um beco sem saída e ter que voltar. Não há bala de prata.
Giorgio
2
@Giorgio - grandes reescritas acontecem porque decisões únicas são refletidas em muitos lugares no código. Quando a decisão muda, o código muda em muitos lugares. A refatoração contínua atenua esse problema, reduzindo a duplicação e limitando a quantidade de código que reflete uma única decisão. Uma boa cobertura de teste suporta refatoração contínua.
Kevin cline
7

Em primeiro lugar, o custo inicial não é tão alto quanto você pensa . Sim, você passa mais tempo lidando com testes do que se não fizer nenhum teste. Mas se você fizer um método "teste depois", o que realmente está desperdiçando? Digamos que o TDD leva 10 horas e o DDT leva 6 horas. Seus custos iniciais "extras" são de apenas 4 horas. Agora, se você aplicar uma métrica ou requisito de código, como 90% de cobertura, seu TDD e DDT ficarão ainda mais próximos em termos de custos.

Você escreverá menos código de buggy com o TDD. Mesmo que seja apenas porque você especificou os requisitos como teste, no final do dia você pode provar que seu código está fazendo exatamente o que você queria. Agora, talvez você queira fazer a coisa errada, mas isso não é um bug que é uma solicitação de alteração. Isso é importante. É mais fácil "vender" um produto que está funcionando, mas poderia funcionar de maneira diferente / melhor; é vender um produto que é percebido como não funcionando. Com o TDD, é literalmente impossível passar no teste e escrever código que não está funcionando. É possível que você não tenha entendido os requisitos e que tenha escrito o código errado, mas funcionando.

TDD é melhor quanto mais antiga a base de código fica. Tente refatorar sem um conjunto de testes ou com um pouco implementado. Mesmo uma simples mudança pode introduzir bugs. Ter um conjunto de testes com boa cobertura garante que, à medida que o produto evolua, ele continue funcionando como deveria. Também ajuda a destacar regras comerciais conflitantes (que acontecem por períodos mais longos).

Você não sabe que não funciona. Sem um conjunto de testes, você não sabe se o seu código está funcionando da maneira que você pensa ou se parece estar funcionando.

var foo = function(in) {
    if(in == 0) {
      return true
    }
}

Agora, em todo o seu aplicativo, você chama if(foo()){ doStuff() }O que acontece quando eu reparo o foo?

var foo = function(in) {
    if(in === 0) {
      return true
    }
}

Você deve refatorar e SECAR seus testes também. Um bom conjunto de testes não é difícil de manter. Com testes atômicos bem escritos, raramente tive que voltar e alterar mais de um a dois deles por vez. Quando houve mudanças maiores no conjunto de testes, é uma bandeira vermelha gigante que algo não está certo.

Só não vejo como devo conhecer todos os meus métodos e como as coisas funcionarão até que meu código esteja lá.

Bem, você não deveria. Você deveria escrever um teste para testar se alguma unidade de trabalho está concluída. Se você sente que está testando coisas sobre as quais não pode saber, está pensando muito alto, OU o teste é muito pequeno.

Por exemplo, você precisa saber que uma porta se fecha e se tranca. Eu testaria door.close () e door.lock () e que door.open () retorna false quando a porta está trancada. É isso. Se seus testes são door.lock () define um sinalizador no banco de dados. Então você está testando muito pequeno. O teste não precisa saber como o door.lock () funciona, apenas o que faz.

Agora, se você estiver escrevendo um teste que diz house.isSecure () retorna true quando todas as portas e janelas estão trancadas. Sem olhar primeiro para portas ou janelas, você estará pensando demais.

Finalmente, você pode estar olhando para uma unidade de trabalho muito grande . Ao obter sua lista de requisitos, você deve organizá-los para trabalhar na menor unidade. Escreva o teste para apenas essa unidade, depois o código, depois enxágüe e repita.

Em essência, sua compreensão (a lista) de como o TDD deve funcionar está errada. Você nunca deve ter 2/100 de passagem. Você deve ter 1/1 de passagem, depois 2/2, depois 3/3, depois 4/4 e assim por diante.

Uma lista revisada para você

  1. Obtenha todas as especificações
  2. Escolha uma especificação
  3. Teste-o
  4. Codifique
  5. Teste-o
  6. Se os testes forem aprovados, passe para 7, vá para 4
  7. Se você tiver feito todas as especificações, vá para 8 ou vá para 2
  8. Revise as especificações com o consumidor e adicione novas especificações sempre que necessário. Se feito corretamente, você não precisará alterar nenhum teste. Basta adicionar novos. Às vezes, a coleta de requisitos pode desmoronar e você precisa adicionar testes que entram em conflito com os testes anteriores, mas raramente é necessário alterar os testes.
coteyr
fonte
2
Gosto dessa resposta porque destaca as vantagens reais do TDD - tornando as bases de código herdadas gerenciáveis. Muitas pessoas que trabalharam apenas na fase greenfield de um projeto não vêem os benefícios do TDD porque parece que é apenas um trabalho extra. Realmente compensa quando você precisa atualizar e manter uma base de código existente. Desejo que, na escola, eles lhe dêem um projeto e, assim que você obtê-lo funcionando, solicite várias alterações de recursos. Isso espelharia mais de perto a realidade e demonstraria o valor do TDD.
thomij
1
Eu também gosto desta resposta. Observe que tendemos a esquecer quanto tempo realmente gastamos executando testes manualmente: editar, compilar, avançar para depurar manualmente, repetir. O TDD automatiza muito disso.
Technophile
O que me interessa aqui é que é importante observar que, quando você escreve um teste de unidade no TDD, não está escrevendo um teste para uma parte da especificação funcional do cliente, está escrevendo um teste para o comportamento do unidade como você - um programador - definiu em seu cérebro, ou seja, "essa classe deve gerar um evento quando X acontecer, vamos escrever um teste para isso". Esta vaga "seus testes são suas especificações!" a retórica é ótima quando você já tem TDD, mas para alguém fora do circuito, ela levanta mais perguntas do que respostas.
Ant # P de
4

Há algumas chaves que sinto que as outras respostas estão faltando.

Três vantagens

Primeiro, o custo do erro e o custo da mudança são tão incrivelmente diferentes entre o software e uma empresa que algumas regras podem não se aplicar. Por exemplo, o custo de testar uma estrutura física é tão alto que você precisa de um alto grau de confiança para que ela funcione para não desperdiçar o teste. Com o software, se você pode executar um conjunto de testes de unidade em 1 a 5 segundos, temos opções diferentes à nossa disposição.

Em segundo lugar, o objetivo de executar um teste que você espera falhar antes de escrever o código em teste é verificar o próprio teste. Claro que pode parecer bobo ou inútil. Mas já vi testes de unidade suficientes escritos de uma maneira que sempre será aprovada, mesmo quando o código em teste está quebrado. Isso pode acontecer facilmente quando você escreve o código em teste, o teste manualmente e depois o teste de unidade. Se você escreve um teste de unidade, vê-o falhar, quando escreve o código necessário para fazê-lo passar, e passa, você sabe que seu teste está correto. Se o código em teste regredir, há uma chance razoável de que seu teste de unidade o pegue.

Uma terceira vantagem do TDD, não mencionada com frequência, é que o tamanho e a complexidade do código resultante costumam ser uma ordem de magnitude menor. Sempre me orgulhei de preferir códigos simples e concisos. Até eu começar a praticar TDD. O TDD me mostrou que o que eu teria feito antes seria código excessivo. Por que isso acontece? Como você escreve um teste, escreva um código para fazer o teste passar. Quando o teste passa, você termina. Se você estiver nessa mentalidade, é difícil escrever acidentalmente código "extra".

(Código extra foi um problema que eu observei em um produto em que eu costumava trabalhar. Problemas graves provenientes de código que ninguém solicitou, mas alguns desenvolvedores acharam que seria legal.)

Análise de custos

Então, de certa forma, você está certo. Não conseguimos escapar dessa estratégia ao construir uma casa. Seria muito caro. Mas o software não é uma casa. Software é barato.

Uma analogia com uma casa é o trabalho de montar a casa versus um compilador de software.

Em um mundo não TDD, os desenvolvedores ainda repetem. Seguimos um código -> compilar -> executar -> ciclo de teste. Já estamos em um modelo que se desvia substancialmente da construção de uma casa. Se o pessoal da construção construir uma moldura de porta, construir uma porta e precisar reconstruir a moldura porque a porta é muito grande para isso, você terá um problema de custo. Assim, você gasta mais tempo na frente para garantir que você obtenha tudo perfeito. Na programação, a maioria dos projetos pode ser compilada em segundos ou minutos, portanto, não importa se algo é imperfeito desde o início. O custo de pensar em assuntos triviais antes do tempo geralmente supera o custo de recompilar. Assim, recompilamos o tempo todo.

TDD é o mesmo princípio, apenas rotacionado para que o teste avance na frente. Se o teste puder ser supervalorizado (para que tudo seja executado em segundos), o custo de pensar na solução geral perfeita e global em uma única iteração de codificação do big bang supera o custo da refatoração.

Exceto...

Existem algumas coisas na programação em que esses argumentos não se sustentam. Esse é o objetivo da arquitetura. Identifique e planeje preocupações iniciais que serão mais caras para serem alteradas posteriormente. Por exemplo, você não escolheria um banco de dados em tempo real sem pensar em suas necessidades, começar a construir e simplesmente argumentar que pode alterá-lo mais tarde. Você precisa pensar sobre isso.

Brandon
fonte
1
Ótimo ponto de verificação do teste! Além disso: ao construir uma casa, é difícil mover uma parede (e isso deixa cicatrizes). Com o software, mover o código é relativamente fácil - desde que você tenha um teste automatizado para detectar quaisquer alterações não intencionais.
Technophile
4

Eu costumava pensar muito sobre isso até fazer alguns projetos de TDD. Há uma explicação muito sucinta e abrangente que apareceu para mim enquanto eu fazia isso:

Quando você escreve código, precisa ter alguma maneira de garantir que o código faça algo significativo. Então você testa seu código. Você pode fazer testes ad hoc. No entanto, o teste funciona melhor se você pensar em como fazer o teste antes de começar a fazer as coisas. Então, você projeta a estratégia de teste antes de ir para o teste.

Agora que você está pensando em sua estratégia de teste, você também pode automatizar, pelo menos uma parte dela ... E eis que você tem algum nível de TDD. O fato de você escrever código até passar no teste é normal. É o que você faz de qualquer maneira, escreva código até que funcione. Agora é fácil explodir os testes.

Por razões além do escopo, você não escreve a coisa toda de uma só vez. Portanto, você também não projeta os testes de uma só vez. Mas basicamente é isso que é. Apenas um planejamento melhor dos testes, você nem sempre escreve o teste com antecedência. Às vezes, você adiciona mais à medida que avança e encontra bugs que não antecipava ou torna o teste mais robusto posteriormente. E, às vezes, você pode não estar projetando testes, mas acha trabalhoso o trabalho manual, para fazer os testes mais tarde.

Portanto, o TDD é apenas uma maneira extrema de ver como você valida seu trabalho.

joojaa
fonte
4

Embora essa pergunta já tenha uma resposta aceita, acredito que tenho algo a acrescentar, vindo de um estilo de design sem teste de escrita (todos os testes executados manualmente por "testadores" após um procedimento de teste) para o TDD. Essas são apenas minhas observações pessoais, embora eu acredite que sejam bastante universais.

Quando você está escrevendo algo novo, algo que nunca fez antes, não há diferença significativa entre o TDD e o fato de não fazer o TDD.

Geralmente, o que você faria é escrever um pequeno pedaço de código para explorar uma idéia, adicionar alguns bits codificados para "testar" e compilar e / ou executá-lo. Se funcionar, você excluirá o código codificado e generalizará o código adicionando parâmetros, variáveis ​​de instância etc.

Pense nisso. É exatamente a mesma quantidade de trabalho que o TDD. A única diferença é que no TDD você escreveria os bits de "teste" separadamente em outro arquivo e não os excluiria no final. No TDD, você mantém seu código de teste .

Obviamente, TDD ser um pouco mais organizado significa que há um pouco mais de trabalho para descobrir como separar os bits de código de teste do seu código real. Mas se você escreveu testes de unidade antes, aprenderá a modularizar seu código para testes.

slebetman
fonte
2

Por que o ágil tem tudo a ver com desenvolvimento orientado a teste (TDD) e não teste orientado a desenvolvimento (DDT)?

Apenas entrando aqui porque acho que a pergunta sugere um fato ("ágil é tudo sobre TDD") que considero bastante questionável. Todas as respostas parecem levar esse fato como garantido. São boas respostas se você assumir que o ágil é principalmente sobre TDD (também conhecido como teste no nível da unidade).

https://en.wikipedia.org/wiki/Agile_software_development#Agile_methods lista uma boa dúzia ou mais de modelos de desenvolvimento diferentes.

Eu ofereceria especialmente o Desenvolvimento Orientado ao Comportamento e o Desenvolvimento Orientado a Recursos como alimento para o pensamento. Bestas completamente diferentes, que também podem levar a todos os benefícios do TDD básico, mas estão muito longe de um simples ciclo de refator vermelho-verde.

Então, minha resposta é:

  • "DDT" (também conhecido como escrever testes após ou "no topo" da implementação) não funciona por razões da vida real; assim que você recebe pressão de tempo ou dinheiro, os testes saem pela janela; e apenas fazer testes para o benefício de ter testes é bastante interessante, IMO.
  • Agile não é tudo sobre desenvolvimento orientado a teste (TDD) se você interpretar esse termo basicamente como "testes de unidade para todos os blocos de construção / classes" (que, pelo menos, de acordo com a Wikipedia, é o caso). TDD é apenas uma possibilidade de desenvolvimento ágil.
  • Existem outros métodos, como BDD ou FDD, que fazem uma abordagem de pilha cheia. Você ainda escreve seus cenários antecipadamente, ainda possui um ciclo de refatoração verde-vermelho e ainda implementa até que seus cenários sejam verdes, mas seus "testes" por definição exercem todo o software (agindo como interação do usuário) e nunca apenas uma única unidade.

Prefiro ter um aplicativo com cobertura completa de BDD / FDD e sem testes de unidade do que um com cobertura completa de teste de unidade e sem testes de pilha completa.

(O TDD tem seu lugar, é claro, por exemplo, em APIs, mas não é disso que estamos falando aqui.)

Novamente, não estou tentando rebater todas as outras respostas aqui, apenas apontando que a pergunta é formulada bastante estreita e que o campo tem muito mais a oferecer.

AnoE
fonte
Eu conhecia o outro, mas, na época, escrever esse ddt parecia fazer mais sentido. Gostaria de saber por que o TDD é melhor para a API? Como o utilizaria para dizer que um cliente da Web com MVC difere?
Nerdlyist
1
Nas APIs, é extremamente importante que elas sigam um contrato muito detalhado ; cada chamada de método deve se comportar de maneira previsível para os usuários (que são outros programas) para usá-la corretamente. É aqui que o teste de unidade realmente brilha. OTOH, em um aplicativo, é mais importante para os recursos que seus usuários humanos precisam para funcionar como pretendido. Claro, é melhor que seus "internos" também estejam corretos, mas é mais importante ter uma boa cobertura da funcionalidade "ponta a ponta" (ou seja, da interface do usuário até o banco de dados e vice-versa) em vez de testar cada método individual (que os usuários finais humanos não verão nada).
AnoE
Obviamente, tudo está em graus diferentes aqui - pode fazer sentido ter um punhado de testes de unidade aqui também, se você tiver métodos / classes muito complicados em algum lugar (por exemplo: calcular distâncias entre geocoordenados); mas o volume (por exemplo, em aplicativos de negócios) é o fluxo de trabalho geral.
AnoE
0

Embora você não o veja escrito dessa maneira com frequência, grande parte do raciocínio por trás do Agile é que reescrever o código é melhor do que escrevê-lo bem na primeira vez. Toda vez que você reescreve o código, aprimora o código e aprimora a si mesmo. Acertar na primeira vez tende a ser mais lento e quebradiço.

A analogia da casa não é tão ruim assim, mas é preciso pensar quanto tempo sabemos sobre construção de casas versus quanto tempo sabemos sobre engenharia de software - também a complexidade da engenharia de software está mais próxima de construir uma ponte longa ou torre de 20 andares do que uma casa. Também devemos considerar que, com novas linguagens e ferramentas sendo construídas, é como se todo construtor quisesse construir com formas, tamanhos e materiais completamente diferentes. Caos completo.

No entanto, inspecionar não é uma boa analogia ao teste de código. No software, nem chegamos ao ponto de ter inspetores decentes (exceto seus colegas de vários níveis de habilidade). Também não temos regulamentos a serem inspecionados, exceto, talvez, alguns "padrões de codificação" arbitrários (que são mais parecidos com testar a cor da sua pintura e o layout do gramado do que a estrutura).

O teste primeiro é mais como você constrói um muro e aplica alguma pressão para garantir a estabilidade antes de levantá-lo e colocá-lo em sua casa. É como medir a janela para garantir que ela caiba no buraco que você deixou antes de tentar colocá-la.

Se você tivesse que construir uma série de pontes sem nossos séculos de conhecimento, padrões, regulamentos e matemática de arquitetura anteriores, que atualmente temos para provar que nossas pontes funcionarão antes de construí-las, você provavelmente estará testando e reconstruindo muito suas pontes.

Finalmente, um ponto de não analogia - com o software toda vez que você pode reescrever uma seção do seu código, você melhora significativamente seu código e sua habilidade. Aproveite todas as chances que puder para reescrever o código. TDD pode ser uma ótima desculpa.

Bill K
fonte