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.
Dê-me todas as suas especificações (histórias).
Obtenha aprovação das especificações.
Divida todas as especificações em inspeção que acho que vou precisar (veja no futuro).
Ligue para um inspetor para examinar esses pontos e me diga agora que estou reprovando na inspeção (gee, obrigado).
Comece a construir a casa.
Ligue para o inspetor de volta diariamente (passando em 2/100).
Oh, dispara, houve um problema com meu entendimento e agora preciso adicionar mais 9 inspeções e alterar 27 delas.
Inspetor de chamada passando em 1/109.
Droga. Por que o inspetor não gosta deste? Atualizei o nome do método ...
Construa um pouco mais.
UGGGGHHHH MAIS ALTERAÇÕES, deixe-me atualizar o maldito inspetor. Oh, eu estou falhando, não s ** t.
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:
Dê-me todas as suas especificações (histórias).
Obtenha aprovação das especificações e divida-as.
Inicie uma unidade (a base).
Faça anotações (comentários) de alguma lógica complicada.
No final, antes de iniciar a próxima unidade, faça a inspeção (crie um teste).
Corrija qualquer problema encontrado e inspecione novamente.
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.
fonte
Respostas:
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.
fonte
Software não é uma casa. A intuição é boa, mas entenda que nem sempre é correta.
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).
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.
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.
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.
fonte
open_door
, não preciso passar uma bandeiratoilet_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.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.
fonte
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.
fonte
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.
Agora, em todo o seu aplicativo, você chama
if(foo()){ doStuff() }
O que acontece quando eu reparo o foo?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.
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ê
fonte
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.
fonte
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.
fonte
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.
fonte
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 é:
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.
fonte
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.
fonte