Por que é ágil tudo sobre o desenvolvimento orientado a testes (TDD) e não o teste orientado a desenvolvimento (DDT)?

74

Por isso, sou novo no agile, mas não no desenvolvimento orientado a testes . Meus professores na faculdade eram todos sobre a ideia de testes, depois o código e depois os testes. Não tenho certeza se entendi o porquê. Do meu ponto de vista, é muito custo inicial que provavelmente será alterado à medida que seu código evoluir.

É assim que imagino o TDD e porque me confunde. Se eu fosse para construir uma casa como um contratante de TDD.

  1. Me dê todas as suas especificações (histórias).

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

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

  4. Chame um inspetor para olhar para esses pontos e diga-me agora que estou falhando com a inspeção (obrigada).

  5. Comece a construir a casa.

  6. Chame o inspetor de volta diariamente (passando por 2/100).

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

  8. Inspeção de chamadas em 1/109.

  9. Droga. Por que o inspetor não gosta deste aqui? Ah, eu atualizei o nome desse método ...

  10. Crie mais alguns.

  11. UGGGGHHHH MAIS MUDANÇAS deixe-me atualizar o maldito inspetor. Oh, eu não estou conseguindo, não é **.

  12. Já terminei?

Ok, isso pode ser estranho, mas eu não vejo como eu deveria saber todos os meus métodos e como as coisas funcionarão até que meu código esteja lá. 99% do tempo eu tenho que voltar e atualizar um teste de unidade de qualquer maneira e adicionar mais como eu vou. Apenas parece para trás.

O que parece mais apropriado é o DDT ou o teste orientado para o desenvolvimento, que é uma coisa que a comunidade esquece completamente.

Pelo que entendi, DDT para uma casa seria:

  1. Me dê todas as suas especificações (histórias).

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

  3. Inicie uma unidade (a fundação).

  4. Tome notas (comentários) de alguma lógica complicada.

  5. No final, antes de começar, a próxima unidade tem a inspeção (criar um teste).

  6. Corrija qualquer problema encontrado e inspecione novamente.

  7. Aprovada esta unidade passar para a próxima.

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

    
por nerdlyist 03.08.2016 / 16:19
fonte

10 respostas

83

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

Então, em sua primeira analogia, você não escreveria 100 testes, pois não há como saber como será seu 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 estratégia esbanjadora no começo, quando é óbvio o que seu código deve fazer, mas a melhor coisa com essa 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 em pares; um par escreve o teste, o próximo escreve o código para fazê-lo passar, depois escreve o próximo teste e assim por diante.

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

Depois de entrar na mentalidade, é muito difícil não estar nele e codificar o modo "antiquado".

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

    
por 03.08.2016 / 17:58
fonte
84

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

Break down all the specs into inspection I think I will need (see into the future).

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

Call the inspector back out daily (passing 2/100).

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

Além disso, a analogia "chame o inspetor para fora" implica que leva algum tempo para a pessoa sair e fazer seu trabalho. A execução de testes de unidade para uma iteração de TDD deve demorar alguns segundos.

It seems like changes can be made faster and without the overhead TDD seem to create.

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

O problema do desenvolvedor primeiro é, às vezes, quando você começa a testar, você descobre que existe um problema grande . Como você coloca a cama ao lado do banheiro e ninguém quer viver lá tipo de problema. Coisas que levam mais tempo para consertar do que qualquer sobrecarga de TDD. Coisas que o TDD teria detectado, já que o TDD obriga você a usar seu código desde o início e pensar em como interagir com ele.

My professors in college were all about the idea of Tests then Code then Test.

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

    
por 03.08.2016 / 16:37
fonte
12

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

Dito isso, há uma distinção enorme que vale a pena ressaltar:

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

No exemplo da construção de uma casa, os requisitos e testes do precedem a construção física. E partes do conjunto de testes são executadas continuamente - por vários agentes, mesmo! Quando o construtor pega um 2x4, ele imediatamente e automaticamente "testa" a unidade contra sua noção de "como um som 2x4 se parece". Ele não cria requisitos depois que ele pega; 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 pré-existente , elas falharão.

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 pré-existente de condições em que a estrutura será finalmente testada quando o buildout estiver completo.

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 lenha para uma caixa de brinquedos ou algo assim, deixando a intuição e a criatividade guiá-lo, isso não é um rigoroso trabalho em madeira TDD. Nesse caso, você está basicamente confiando nas leis físicas do meio e em suas expectativas para validar o trabalho (ou seja, "se ele compilar e funcionar, é bom!").

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

tl; dr

Mesmo que a construção! = software de gravação: Construção funciona de forma orientada a testes. As condições de "passagem" para cada unidade do precedem o seu buildout.

Não combine "executar testes" com "testes de escrita".

Nem tudo precisa ser TDD.

    
por 03.08.2016 / 17:16
fonte
11

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

Agora, com casas reais, o arquiteto cria esses planos na frente. Então você chama os construtores, que começam a construir, se deparam com problemas, têm que emendar, entrar no controle de construção, querem mudanças, etc. Então, no final, o arquiteto volta e cobra mais para atualizar seus desenhos para refletir o que aconteceu. Esta é uma maneira péssima de fazer as coisas, mas as casas demoram muito tempo para construir e são caras, então é a única maneira prática.

As coisas são melhores para engenharia de software. O equivalente à construção da casa é 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 repetidamente a casa à 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, poderá fazer com que o inspetor faça uma nova inspeção de cada vez que você reconstruir.

Se você segue um TDD estrito, ou uma abordagem mais orientada para o teste de escrever algum código, então alguns testes realmente não importam. 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ê quer mudar o código, avisando-o de quebras na funcionalidade em outro lugar quando você muda alguma coisa.

    
por 03.08.2016 / 17:21
fonte
7

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

Você escreverá um código com menos bugs com o TDD. Mesmo que seja porque você especificou os requisitos como um teste, no final do dia você pode provar que seu código está fazendo exatamente o que você queria que isso acontecesse. Agora talvez você quisesse que fizesse a coisa errada, mas isso não é um bug que é uma solicitação de mudança. Isso é importante. É mais fácil "vender" um produto que está funcionando, mas pode funcionar de forma diferente / melhor, então é 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 funciona. É possível que você não tenha entendido os requisitos e tenha escrito o código errado, mas funcionando.

O TDD é melhor quanto mais antigo for o código-base. Tente refatorar sem um conjunto de testes ou com um pacote mal implementado. Mesmo uma simples mudança pode introduzir erros. Ter uma suíte de testes com boa cobertura garante que, à medida que o produto evolui, 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 uma suíte de testes, você não sabe se o seu código está funcionando como 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 corri 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 mudar mais de 1-2 deles de cada vez. Quando houve mudanças maiores na suíte de testes, é uma gigantesca bandeira vermelha que algo não está certo.

I just do not see how I should know all my methods and how things will work until my code is there.

Bem, você não deveria. Você deveria escrever um teste que testa que alguma unidade de trabalho é feita. Se você sente que está testando coisas que você não pode saber, então você está pensando muito grande, ou o teste OR é muito pequeno.

Por exemplo, você precisa saber que uma porta se fecha e trava. Gostaria de testar door.close () e door.lock () e que door.open () retorna false quando a porta está bloqueada. É isso. Se os 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 isso.

Agora, se você está escrevendo um teste que diz que house.isSecure () retorna verdadeiro quando todas as portas e janelas estão bloqueadas. Sem olhar para as portas ou janelas primeiro, então você está pensando muito grande.

Por fim, você pode estar olhando para uma unidade de trabalho muito grande . Quando você recebe sua lista de requisitos, você deve organizá-los para trabalhar na unidade mais pequena. Escreva o teste apenas para aquela unidade, depois o código, depois enxágüe e repita.

Em essência, o seu entendimento (a lista) de como o TDD deve funcionar está desativado. Você nunca deve passar 2/100. Você deve ter 1/1 de passe, 2/2 de passe, 3/3 de passe, 4/4 de passe e assim por diante.

Uma lista revisada para você

  1. Obtenha todas as especificações
  2. Escolha uma especificação
  3. Teste
  4. Codifique
  5. Teste
  6. Se os testes passarem para 7, vá para 4
  7. Se você tiver feito todas as especificações, vá para 8 e vá para 2
  8. Revise as especificações com o consumidor e adicione novas especificações quando necessário. Se feito corretamente, você não deve ter que alterar nenhum teste. Basta adicionar novos. Às vezes, a coleta de requisitos pode se desfazer e você precisa adicionar testes que entram em conflito com testes anteriores, mas raramente é necessário alterar os testes.
por 03.08.2016 / 21:12
fonte
4

Existem algumas chaves que sinto que faltam outras respostas.

Três vantagens

Primeiro, o custo do erro e o custo da mudança é tão incrivelmente diferente entre o software e a casa 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 trabalhar de forma a não desperdiçar testes. Com o software, se você puder executar um conjunto de testes de unidade em 1 a 5 segundos, teremos opções diferentes à nossa disposição.

Em segundo lugar, o propósito de executar um teste que você espera falhar antes de escrever o código em teste é verificar o teste em si. Claro que pode parecer bobo ou um desperdício. Mas eu já vi suficientes testes unitários escritos de uma forma que sempre passará, mesmo quando o código em teste está quebrado. Isso pode acontecer facilmente quando você escreve o código em teste, testa-o manualmente e escreve o teste da unidade. Se você escrever um teste de unidade, vê-lo falhar, então quando você escreve o código necessário para fazê-lo passar, e ele passa, você sabe que seu teste é bom. Se o código sob teste regride, há uma chance razoável de que seu teste unitário o capture.

Uma terceira vantagem do TDD, não mencionada com frequência, é que o tamanho e a complexidade do código resultante geralmente é uma ordem de grandeza menor. Eu sempre me orgulhei de preferir código simples e conciso. Até eu começar a praticar o TDD. TDD me mostrou que o que eu teria feito antes seria código excessivo. Por que isso acontece? Porque você escreve um teste, então escreva o código para fazer o teste passar. Quando o teste passa, você está feito com isso. Se você estiver nessa mentalidade, é difícil escrever acidentalmente código "extra".

(Código extra tem sido um problema que tenho observado em um produto que eu costumava trabalhar. Problemas graves vindos do código que ninguém pediu, mas alguns desenvolvedores acharam que seria legal.)

Análise de custo

Então, de certa forma, você está certo. Nós não poderíamos nos safar dessa estratégia quando construímos uma casa. Seria muito caro. Mas o software não é uma casa. O 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 interagem. Nós 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 as pessoas da sua construção construírem um batente de porta, depois construírem uma porta e depois reconstruírem a estrutura porque a porta é grande demais para ela, você terá um problema de custo. Assim, você gasta mais tempo na frente para garantir que tudo fique perfeito. Na programação, a maioria dos projetos pode ser compilada em segundos ou minutos, portanto, não importa se algo é imperfeito no início. O custo de pensar em assuntos triviais antes do tempo normalmente supera o custo da recompilação. Assim, nós recompilamos o tempo todo.

O TDD é o mesmo princípio, apenas girado para que o teste seja adiantado. Se o teste puder ser feito de forma super barata (para que tudo corra em segundos), então o custo de pensar sobre a solução geral e perfeita em uma única iteração de codificação 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 propósito da arquitetura. Identifique e planeje as preocupações iniciais que serão mais caras para serem alteradas posteriormente. Por exemplo, você não escolheria um banco de dados rapidamente sem pensar em suas necessidades, começar a construir e simplesmente argumentar que pode alterá-lo mais tarde. Você precisa pensar sobre isso.

    
por 03.08.2016 / 19:02
fonte
4

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

Quando você escreve um código, você tem que ter certeza de que o código faz 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 testar antes de começar a fazer as coisas. Então você projeta a estratégia de teste antes de ir para o teste.

Agora, como você está pensando na sua estratégia de testes, você pode também 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. Isso é o que você faz de qualquer maneira, escreva o código até que funcione. Agora é fácil explodir os testes.

Por razões além do escopo, você não escreve tudo de uma só vez. Então você não projeta os testes de uma só vez também. Mas basicamente é o que é. Apenas um melhor planejamento de testes, você nem sempre escreve teste na frente. Às vezes, você adiciona mais conforme progride e encontra bugs que você não previu ou torna o teste mais robusto mais tarde. E às vezes você pode não fazer testes, mas achar que o teste manual é trabalhoso para que você faça os testes mais tarde.

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

    
por 03.08.2016 / 21:00
fonte
4
Embora esta questão já tenha uma resposta aceita, acredito que eu tenha algo a acrescentar, vindo de um estilo de design sem teste escrito (todos os testes realizados manualmente por "testadores" seguindo um procedimento de teste) para TDD. Estas são apenas minhas observações pessoais, embora eu acredite que elas sejam bem universais.

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

Geralmente, o que você faz é escrever um pequeno código para explorar uma ideia e depois adicionar alguns bits codificados para "testar" e depois compilá-los e / ou executá-los. Se funcionar, você excluirá o material 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 "testing" separadamente em outro arquivo e não os excluiria no final. Em TDD você mantém seu código de teste .

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

    
por 04.08.2016 / 07:31
fonte
2

Why is agile all about the test-driven development (TDD) and not development-driven test (DDT)?

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

link lista uma boa dúzia ou mais modelos de desenvolvimento diferentes.

Eu ofereço especialmente Desenvolvimento Dirigido por Comportamento e Desenvolvimento Orientado a Característica como alimento para o pensamento. Animais 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 refator vermelho-verde.

Então minha resposta é:

  • "DDT" (também, escrever testes após ou "no topo" da implementação) não funciona por razões reais; assim que você ganha tempo ou dinheiro, os testes saem da janela; e apenas ter testes para o benefício de ter testes é mais ou menos assim, IMO.
  • Agile não é tudo sobre desenvolvimento orientado a testes (TDD) se você interpretar esse termo basicamente como "testes unitários para todos os blocos / classes de construção" (que, pelo menos de acordo com a Wikipedia, é O caso). O TDD é apenas uma possibilidade de desenvolvimento ágil.
  • Existem outros métodos, como o BDD ou o FDD, que fazem uma abordagem de pilha completa. Você ainda escreve seus cenários na frente, você ainda tem um ciclo red-green-refactor e você só implementa até que seus cenários sejam verdes, mas seus "testes" por definição exercitam todo o software (agindo como interação com o usuário) e nunca apenas uma única unidade.

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

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

Mais uma vez, não estou tentando minimizar todas as outras respostas aqui, apenas apontando que a questão é formulada de forma bastante limitada, e o campo tem muito mais a oferecer.

    
por 04.08.2016 / 18:30
fonte
0

Embora você não o veja assim muitas vezes, muito 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, você melhora o código e melhora a si mesmo. Obtê-lo "certo" pela primeira vez tende a ser mais lento e mais frágil.

A analogia da casa não é tão ruim assim, mas é preciso pensar em 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 da construção uma ponte longa ou uma torre de 20 andares que uma casa. Também devemos considerar que, com novas linguagens e ferramentas sendo construídas, é como se todo construtor quisesse construir o edifício com formas, tamanhos e materiais completamente diferentes. Caos completo.

No entanto, a inspeção não é uma boa analogia ao teste de código. Em software, não estamos nem no ponto em que temos inspetores decentes (exceto seus colegas de vários níveis de habilidade). Também não temos regulamentos para inspecionar, exceto, talvez, alguns "padrões de codificação" quase arbitrários (que é mais como testar a cor da tinta e o layout do gramado do que a estrutura).

O teste em primeiro lugar é mais como se você construísse uma parede e aplicasse alguma pressão nela para garantir a estabilidade antes de levantá-la e colocá-la em sua casa. É como medir a janela para garantir que ela se encaixe 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 prévio de arquitetura, padrões, regulamentações e matemática que atualmente temos para provar que nossas pontes funcionarão antes de construí-las, provavelmente você estaria testando e reconstruindo suas pontes MUITO.

Finalmente, um ponto de não-analogia - com o software toda vez que você tem permissão para reescrever uma seção do seu código, você melhora tanto o seu código quanto sua habilidade de forma significativa. Aproveite todas as chances possíveis para reescrever o código. TDD pode ser uma ótima desculpa.

    
por 03.08.2016 / 18:54
fonte