Quando você escreve o código "real" no TDD?

147

Todos os exemplos que li e vi nos vídeos de treinamento têm exemplos simplistas. Mas o que eu não vejo se como faço o código "real" depois de ficar verde. Esta é a parte "Refatorar"?

Se eu tiver um objeto bastante complexo com um método complexo, e escrever meu teste e o mínimo necessário para fazê-lo passar (depois que ele falhar pela primeira vez, vermelho). Quando eu volto e escrevo o código real? E quanto código real eu escrevo antes de testar novamente? Suponho que o último seja mais intuição.

Edit: Obrigado a todos que responderam. Todas as suas respostas me ajudaram imensamente. Parece haver idéias diferentes sobre o que eu estava perguntando ou confuso, e talvez exista, mas o que eu estava perguntando era: digamos que tenho um aplicativo para a construção de uma escola.

No meu design, tenho uma arquitetura com a qual quero começar, Histórias de usuários, e assim por diante. A partir daqui, pego essas histórias de usuário e crio um teste para testar a história de usuário. O usuário diz: Temos pessoas matriculadas na escola e pagamos taxas de inscrição. Então, penso em uma maneira de fazer isso falhar. Ao fazer isso, criei uma classe de teste para a classe X (talvez Student), que falhará. Eu então crio a classe "Aluno". Talvez "Escola" eu não sei.

Mas, de qualquer forma, o TD Design está me forçando a pensar na história. Se posso fazer um teste falhar, sei por que ele falha, mas isso pressupõe que eu possa fazer passar. É sobre o design.

Eu comparo isso a pensar em recursão. Recursão não é um conceito difícil. Pode ser mais difícil realmente acompanhar isso em sua mente, mas, na realidade, a parte mais difícil é saber quando a recursão "quebra", quando parar (minha opinião, é claro). Então, tenho que pensar no que para a recursão primeiro. É apenas uma analogia imperfeita, e assume que cada iteração recursiva é um "passe". Mais uma vez, apenas uma opinião.

Na implementação, a escola é mais difícil de ver. Os ledgers numéricos e bancários são "fáceis" no sentido de que você pode usar aritmética simples. Eu posso ver a + b e retornar 0, etc. No caso de um sistema de pessoas, tenho que pensar mais sobre como implementá- lo. Eu tenho o conceito de falha, aprovação, refatoração (principalmente por causa do estudo e dessa pergunta).

O que eu não sei é baseado na falta de experiência, na minha opinião. Não sei como deixar de inscrever um novo aluno. Eu não sei como falhar alguém digitando um sobrenome e sendo salvo em um banco de dados. Eu sei como fazer um +1 para matemática simples, mas com entidades como uma pessoa, não sei se estou apenas testando para ver se recebo de volta um ID exclusivo do banco de dados ou outra coisa quando alguém digita um nome em um banco de dados ou ambos ou nenhum.

Ou, talvez isso mostre que ainda estou confuso.

johnny
fonte
193
Depois do TDD, as pessoas vão para casa a noite toda.
Hbbs
14
Por que você acha que o código que você escreveu não é real?
Goyo
2
@RubberDuck Mais do que as outras respostas. Tenho certeza de que vou me referir a isso em breve. Ainda é meio estranho, mas não vou desistir. O que você disse fazia sentido. Estou apenas tentando fazer sentido no meu contexto ou em um aplicativo comercial regular. Talvez um sistema de inventário ou algo parecido. Eu tenho que considerar isso. Sou grato pelo seu tempo, no entanto. Obrigado.
johnny
1
As respostas já acertam a cabeça, mas desde que todos os seus testes estejam passando e você não precise de novos testes / funcionalidades, pode-se supor que o código que você possui foi concluído, sem fiapos.
ESR
3
Há uma suposição na pergunta que pode ser problemática em "Eu tenho um objeto bastante complexo com um método complexo". No TDD, você escreve seus testes primeiro e começa com um código bastante simples. Isso forçará você a codificar uma estrutura fácil de testar que precisará ser modular. Um comportamento tão complexo será criado combinando objetos mais simples. Se você acabar com um objeto bastante complexo ou método, em seguida, é quando você refatorar
Borjab

Respostas:

243

Se eu tiver um objeto bastante complexo com um método complexo, e escrever meu teste e o mínimo necessário para fazê-lo passar (depois que ele falhar pela primeira vez, vermelho). Quando eu volto e escrevo o código real? E quanto código real eu escrevo antes de testar novamente? Suponho que o último seja mais intuição.

Você não "volta" e escreve "código real". É tudo código real. O que você faz é voltar e adicionar outro teste que o força a alterar seu código para fazer o novo teste passar.

Quanto código você escreve antes de testar novamente? Nenhum. Você escreve código zero sem um teste com falha que o força a escrever mais código.

Observe o padrão?

Vamos examinar outro exemplo simples, na esperança de que isso ajude.

Assert.Equal("1", FizzBuzz(1));

Fácil fácil.

public String FizzBuzz(int n) {
    return 1.ToString();
}

Não é o que você chamaria de código real, certo? Vamos adicionar um teste que força uma mudança.

Assert.Equal("2", FizzBuzz(2));

Poderíamos fazer algo bobo if n == 1, mas pularemos para a solução sã.

public String FizzBuzz(int n) {
    return n.ToString();
}

Legal. Isso funcionará para todos os números que não sejam do FizzBuzz. Qual é a próxima entrada que forçará a alteração do código de produção?

Assert.Equal("Fizz", FizzBuzz(3));

public String FizzBuzz(int n) {
    if (n == 3)
        return "Fizz";
    return n.ToString();
}

E de novo. Escreva um teste que ainda não passou.

Assert.Equal("Fizz", FizzBuzz(6));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    return n.ToString();
}

E agora cobrimos todos os múltiplos de três (que também não são múltiplos de cinco, observaremos e voltaremos).

Ainda não escrevemos um teste para o "Buzz", então vamos escrever isso.

Assert.Equal("Buzz", FizzBuzz(5));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n == 5)
        return "Buzz"
    return n.ToString();
}

E, novamente, sabemos que há outro caso que precisamos resolver.

Assert.Equal("Buzz", FizzBuzz(10));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n % 5 == 0)
        return "Buzz"
    return n.ToString();
}

E agora podemos lidar com todos os múltiplos de 5 que também não são múltiplos de 3.

Até o momento, ignoramos a etapa de refatoração, mas vejo alguma duplicação. Vamos limpar isso agora.

private bool isDivisibleBy(int divisor, int input) {
    return (input % divisor == 0);
}

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

Legal. Agora removemos a duplicação e criamos uma função bem nomeada. Qual é o próximo teste que podemos escrever que nos forçará a alterar o código? Bem, evitamos o caso em que o número é divisível por 3 e 5. Vamos escrever agora.

Assert.Equal("FizzBuzz", FizzBuzz(15));

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n) && isDivisibleBy(5, n))
        return "FizzBuzz";
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

Os testes passam, mas temos mais duplicação. Temos opções, mas vou aplicar "Extrair variável local" algumas vezes para refatorar em vez de reescrever.

public String FizzBuzz(int n) {

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

E cobrimos todas as informações razoáveis, mas e as informações irracionais ? O que acontece se passarmos 0 ou negativo? Escreva esses casos de teste.

public String FizzBuzz(int n) {

    if (n < 1)
        throw new InvalidArgException("n must be >= 1);

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

Isso já está começando a parecer "código real"? Mais importante, em que ponto deixou de ser "código irreal" e passou a ser "real"? Isso é algo para refletir ...

Então, pude fazer isso simplesmente procurando por um teste que sabia que não passaria a cada passo, mas tive muita prática. Quando estou no trabalho, as coisas nem sempre são tão simples e nem sempre sei qual teste forçará uma mudança. Às vezes, escrevo um teste e fico surpreso ao ver que ele já passou! Eu recomendo que você adquira o hábito de criar uma "Lista de testes" antes de começar. Esta lista de teste deve conter todas as entradas "interessantes" que você puder imaginar. Você pode não usá-los todos e provavelmente adicionará casos à medida que avança, mas essa lista serve como um roteiro. Minha lista de testes para o FizzBuzz se pareceria com isso.

  • Negativo
  • Zero
  • 1
  • Dois
  • Três
  • Quatro
  • Cinco
  • Seis (múltiplo não trivial de 3)
  • Nove (3 ao quadrado)
  • Dez (múltiplo não trivial de 5)
  • 15 (múltiplo de 3 e 5)
  • 30 (múltiplo não trivial de 3 e 5)
Pato de borracha
fonte
3
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Maple_shaft
47
A menos que eu esteja entendendo completamente esta resposta: "Poderíamos fazer algo bobo como se n == 1, mas pularemos para a solução sã." - a coisa toda era boba. Se você sabe de antemão que deseja uma função que faça <spec>, escreva testes para <spec> e pule a parte em que você escreve versões que obviamente falham em <spec>. Se você encontrar um erro em <spec>, certifique-se: escreva um teste primeiro para verificar se você pode exercitá-lo antes da correção e observe a aprovação do teste após a correção. Mas não há necessidade de falsificar todas essas etapas intermediárias.
GManNickG
16
Os comentários que apontam as principais falhas nesta resposta e o TDD em geral foram movidos para o bate-papo. Se você está pensando em usar o TDD, leia o 'chat'. Infelizmente, os comentários de "qualidade" estão agora ocultos em uma conversa para os futuros alunos lerem.
user3791372
2
@GManNickG Acredito que o objetivo é obter a quantidade certa de testes. Escrever os testes antecipadamente facilita a perda de casos especiais que devem ser testados, levando a situações que não são cobertas adequadamente nos testes ou essencialmente a mesma situação sendo cobrada sem sentido inúmeras vezes nos testes. Se você pode fazer isso sem essas etapas intermediárias, ótimo! Porém, nem todo mundo pode fazer isso ainda, é algo que requer prática.
hvd 30/07
1
E aqui está uma citação de Kent Beck sobre a refatoração: "Agora que o teste é executado, podemos perceber (como em" tornar real ") a implementação do resumo ()". Ele então passa a alterar uma constante para uma variável. Eu senti que essa citação correspondia muito bem à pergunta.
22430 Chris Heckler
46

O código "real" é o código que você escreve para fazer seu teste passar. Sério . É simples assim.

Quando as pessoas falam sobre escrever o mínimo necessário para tornar o teste verde, isso significa apenas que seu código real deve seguir o princípio YAGNI .

A idéia da etapa de refatoração é apenas para limpar o que você escreveu quando estiver satisfeito por atender aos requisitos.

Desde que os testes que você escreve realmente abranjam os requisitos do seu produto, uma vez que eles são aprovados, o código está completo. Pense nisso, se todos os seus requisitos de negócios tiverem um teste e todos esses testes forem ecológicos, o que mais há para escrever? (Ok, na vida real, não costumamos ter uma cobertura completa de testes, mas a teoria é sólida.)

GenericJon
fonte
45
Os testes de unidade não podem realmente abranger os requisitos do seu produto, mesmo para requisitos relativamente triviais. Na melhor das hipóteses, eles provam o espaço de entrada e saída e a idéia é que você generalize (corretamente) para o espaço total de entrada e saída. Obviamente, seu código poderia ser muito grande switchcom um caso para cada teste de unidade que passaria em todos os testes e falharia em outras entradas.
Derek Elkins
8
O @DerekElkins TDD exige falhas nos testes. Não falha nos testes de unidade.
Taemyr
6
@DerekElkins é por isso que você não apenas escreve testes de unidade, e também porque há uma suposição geral de que você está tentando fazer algo que não seja apenas falso!
jonrsharpe
36
@ Jonrsharpe Por essa lógica, eu nunca escreveria implementações triviais. Por exemplo, no exemplo do FizzBuzz na resposta da RubberDuck (que usa apenas testes de unidade), a primeira implementação claramente "apenas finge". Meu entendimento da questão é exatamente essa dicotomia entre escrever código que você sabe que está incompleto e código que você realmente acredita que implementará o requisito, o "código real". Meu "grande switch" foi concebido como um extremo lógico de "escrever o mínimo necessário para tornar os testes verdes". Eu vejo a pergunta do OP como: onde no TDD está o princípio que evita esse tamanho switch?
Derek Elkins
2
@GenericJon Isso é um pouco otimista demais em minha experiência :) Por um lado, há pessoas que gostam de um trabalho repetitivo sem sentido. Eles ficarão mais felizes com uma declaração de troca gigante do que com uma "tomada de decisão complicada". E para perder o emprego, eles precisariam de alguém que os chamasse da técnica (e é melhor que eles tenham boas evidências de que realmente está perdendo as oportunidades / dinheiro da empresa!) Ou se saem excepcionalmente mal. Depois de assumir a manutenção de muitos desses projetos, posso dizer que é fácil para um código muito ingênuo durar décadas, desde que faça o cliente feliz (e pagando).
Luaan 25/07/19
14

A resposta curta é que o "código real" é o código que faz o teste passar. Se você pode fazer seu teste passar com algo diferente de código real, adicione mais testes!

Concordo que muitos tutoriais sobre TDD são simplistas. Isso funciona contra eles. Um teste muito simples para um método que, digamos, calcula 3 + 8 realmente não tem escolha, mas também computa 3 + 8 e compara o resultado. Isso faz com que pareça que você duplicará todo o código, e esse teste é um trabalho extra inútil e propenso a erros.

Quando você é bom em testes, isso informa como você estrutura seu aplicativo e como você escreve seu código. Se você tiver problemas para apresentar testes úteis e sensíveis, provavelmente deve repensar um pouco o seu design. Um sistema bem projetado é fácil de testar - ou seja, é fácil pensar e implementar testes sensíveis.

Quando você escreve seus testes primeiro, assiste-os a falhar e depois escreve o código que os faz passar, é uma disciplina para garantir que todo o seu código tenha testes correspondentes. Não sigo servilmente essa regra quando estou codificando; muitas vezes escrevo testes após o fato. Mas fazer os testes primeiro ajuda a mantê-lo honesto. Com alguma experiência, você começará a perceber quando estiver se codificando em um canto, mesmo quando não estiver escrevendo testes primeiro.

Carl Raymond
fonte
6
Pessoalmente, o teste que eu escreveria seria assertEqual(plus(3,8), 11), não assertEqual(plus(3,8), my_test_implementation_of_addition(3,8)). Para casos mais complexos, você sempre procura meios de provar o resultado correto, além de calcular dinamicamente o resultado correto no teste e verificar a igualdade.
26617 Steve Jessop
Portanto, para uma maneira realmente boba de fazê-lo neste exemplo, você pode provar que plus(3,8)retornou o resultado correto subtraindo 3 dele, subtraindo 8 dele e verificando o resultado contra 0. Isso é tão obviamente equivalente assertEqual(plus(3,8), 3+8)a ser um um pouco absurdo, mas se o código em teste está criando algo mais complicado do que apenas um número inteiro, pegar o resultado e verificar a exatidão de cada parte geralmente é a abordagem correta. Como alternativa, algo parecido comfor (i=0, j=10; i < 10; ++i, ++j) assertEqual(plus(i, 10), j)
Steve Jessop
... como isso evita o grande medo, que é o de que, ao escrever o teste, cometeremos o mesmo erro no assunto "como adicionar 10" que fizemos no código ativo. Portanto, o teste evita cuidadosamente escrever qualquer código que adicione 10 a qualquer coisa, no teste que plus()pode adicionar 10 às coisas. Ainda dependemos dos valores do loop inicial verificados pelo programador, é claro.
26617 Steve Jessop
4
Só quero salientar que, mesmo que você esteja escrevendo testes após o fato, ainda é uma boa ideia vê-los falhar; encontre alguma parte do código que pareça crucial para o que você está trabalhando, ajuste-o um pouco (por exemplo, substitua um + por um - ou o que for), execute os testes e observe-os falharem, desfaça a alteração e observe-os passar. Muitas vezes eu fiz isso, o teste realmente não falha, tornando-o pior do que inútil: não apenas não está testando nada, mas também me dando falsa confiança de que algo está sendo testado!
Warbo 28/07
6

Às vezes, alguns exemplos sobre TDD podem ser enganosos. Como outras pessoas apontaram antes, o código que você escreve para fazer os testes passarem é o código real.

Mas não pense que o código real parece mágica - está errado. Você precisa entender melhor o que deseja alcançar e, em seguida, escolher o teste de acordo, começando pelos casos mais fáceis e de canto.

Por exemplo, se você precisar escrever um lexer, comece com uma string vazia, depois com um monte de espaços em branco, depois um número, depois com um número cercado por espaços em branco, depois um número errado etc. Essas pequenas transformações levarão você a o algoritmo certo, mas você não pula do caso mais fácil para um caso altamente complexo, escolhido de maneira tola para realizar o código real.

Bob Martin explica perfeitamente aqui .

Victor Cejudo
fonte
5

A parte do refator é limpa quando você está cansado e quer ir para casa.

Quando você está prestes a adicionar um recurso, a parte do refator é o que você altera antes do próximo teste. Você refatora o código para liberar espaço para o novo recurso. Você faz isso quando sabe qual será o novo recurso. Não quando você está apenas imaginando.

Pode ser tão simples quanto renomear GreetImplpara GreetWorldantes de criar uma GreetMomclasse (após adicionar um teste) para adicionar um recurso que imprimirá "Oi mãe".

candied_orange
fonte
1

Mas o código real apareceria no estágio de refatoração da fase TDD. Ou seja, o código que deve fazer parte do lançamento final.

Os testes devem ser executados toda vez que você fizer uma alteração.

O lema do ciclo de vida do TDD seria: REFINADOR VERDE VERMELHO

VERMELHO : Escreva os testes

VERDE : Faça uma tentativa honesta de obter código funcional que passe nos testes o mais rápido possível: código duplicado, variáveis ​​com nomes obscuros, hacks da mais alta ordem, etc.

REFATOR : Limpe o código, nomeie corretamente as variáveis. SECAR o código.

graeme
fonte
6
Sei o que você está dizendo sobre a fase "Verde", mas implica que os valores de retorno de cabeamento rígido para fazer os testes passarem podem ser apropriados. Na minha experiência, "Verde" deve ser uma tentativa honesta de fazer com que o código funcione para atender aos requisitos, pode não ser perfeito, mas deve ser tão completo e "entregável" quanto o desenvolvedor pode gerenciar em uma primeira passagem. Provavelmente, é melhor fazer a refatoração algum tempo depois, depois de fazer mais desenvolvimento e os problemas com a primeira passagem se tornarem mais aparentes e surgirem oportunidades de DRY.
Mcottle
2
@mcottle: você pode se surpreender com quantas implementações de um repositório get-only podem ser valores codificados na codebase. :)
Bryan Boettcher
6
Por que eu escreveria um código porcaria e o limparia, quando posso criar um código de qualidade de produção quase tão rápido quanto consigo digitar? :)
Kaz
1
@ Kaz Porque desta forma você corre o risco de adicionar um comportamento não testado . A única maneira de garantir um teste para todo e qualquer comportamento desejado é fazer a simples alteração possível, independentemente de quão ruim seja. Às vezes, o seguinte refatoração traz uma nova abordagem que você não pensou com antecedência ...
Timothy Truckle
1
@ TimothyTruckle O que leva 50 minutos para encontrar a alteração mais simples possível, mas apenas 5 para encontrar a segunda alteração mais simples possível? Vamos com o segundo mais simples ou continuamos procurando pelo mais simples?
Kaz
1

Quando você escreve o código "real" no TDD?

A fase vermelha é onde você escreve o código.

Na fase de refatoração , o objetivo principal é excluir o código.

Na fase vermelha , você faz qualquer coisa para tornar o teste o mais rápido possível e a qualquer custo . Você ignora completamente o que já ouviu falar de boas práticas de codificação ou de padrões de design. Tornar o teste verde é tudo o que importa.

Na fase de refatoração , você limpa a bagunça que acabou de fazer. Agora, verifique primeiro se a alteração que você acabou de fazer é do tipo mais importante na lista Prioridade de Transformação e se existe alguma duplicação de código que você pode remover com maior probabilidade aplicando um padrão de design.

Finalmente, você melhora a legibilidade renomeando identificadores e extrai números mágicos e / ou seqüências literais para constantes.


Não é refatorador de vermelho, é refatorador de verde-vermelho. - Rob Kinyon

Obrigado por apontar para isso.

Portanto, é a fase verde em que você escreve o código real

Na fase vermelha , você escreve a especificação executável ...

Timothy Truckle
fonte
Não é refatorador de vermelho, é refatorador de verde-vermelho. O "vermelho" é levar o conjunto de testes de verde (todos os testes passam) para vermelho (um teste falha). O "verde" é o local onde você desvia seu conjunto de testes de vermelho (um teste falha) para verde (todos os testes passam). O "refator" é onde você pega seu código e o torna bonito enquanto mantém todos os testes aprovados.
27417 Rob Robe Kinyon
1

Você está escrevendo Código Real o tempo todo.

Em cada etapa, você está escrevendo um código para satisfazer as condições que o seu código atenderá aos futuros chamadores do seu código (que podem ser você ou não ...).

Você pensa que não está escrevendo código útil ( real ), porque em um momento você pode refatorá-lo.

A refatoração de código é o processo de reestruturação do código de computador existente - alterando o fatoramento - sem alterar seu comportamento externo.

O que isso significa é que, embora Você esteja alterando o código, as condições atendidas pelo código permanecem inalteradas. E as verificações ( testes ) que você implementou para verificar se seu código já estão lá para verificar se suas modificações mudaram alguma coisa. Então o código que você escreveu o tempo todo está lá, apenas de uma maneira diferente.

Outro motivo para você pensar que não é um código real, é que você está fazendo exemplos em que o programa final já pode ser previsto por você. Isso é muito bom, pois mostra que você tem conhecimento sobre o domínio que você está programando em.
Mas muitas vezes os programadores estão em um domínio que é novo , desconhecido para eles. Eles não sabem qual será o resultado final e o TDD é uma técnica para escrever programas passo a passo, documentando nosso conhecimento sobre como esse sistema deve funcionar e verificando se nosso código funciona dessa maneira.

Quando li The Book (*) no TDD, para mim, o recurso mais importante que se destacou foi a lista: TODO. Isso me mostrou que o TDD também é uma técnica para ajudar os desenvolvedores a se concentrarem em uma coisa de cada vez. Portanto, esta também é uma resposta para sua pergunta sobre quanto código real escrever ? Eu diria código suficiente para focar em uma coisa de cada vez.

(*) "Test Driven Development: By Example" de Kent Beck

Robert Andrzejuk
fonte
2
"Test Driven Development: By Example" por Kent Beck
Robert Andrzejuk 27/07/17
1

Você não está escrevendo código para fazer seus testes falharem.

Você escreve seus testes para definir como deve ser o sucesso, o que deve falhar inicialmente, porque você ainda não escreveu o código que será aprovado.

O ponto principal sobre a criação de testes com falha inicial é fazer duas coisas:

  1. Cobrir todos os casos - todos os casos nominais, todos os casos extremos, etc.
  2. Valide seus testes. Se você apenas os vê passar, como pode ter certeza de que eles reportarão uma falha de maneira confiável quando ocorrer uma?

O ponto por trás do refator vermelho-verde é que escrever os testes corretos primeiro dá a você a confiança de saber que o código que você escreveu para passar nos testes está correto e permite refatorar com a confiança de que seus testes o informarão assim que algo quebra, para que você possa voltar imediatamente e consertá-lo.

Na minha própria experiência (C # /. NET), o puro teste primeiro é um ideal inatingível, porque você não pode compilar uma chamada para um método que ainda não existe. Portanto, "testar primeiro" é realmente sobre a codificação de interfaces e implementações de stub, primeiro, e depois a gravação de testes nos stubs (que falharão inicialmente) até que os stubs sejam aprimorados adequadamente. Eu nunca estou escrevendo "código com falha", apenas construindo a partir de stubs.

Zenilogix
fonte
0

Eu acho que você pode estar confuso entre testes de unidade e testes de integração. Acredito que também pode haver testes de aceitação, mas isso depende do seu processo.

Depois de testar todas as pequenas "unidades", você as testará todas montadas ou "integradas". Geralmente, é um programa ou biblioteca inteira.

No código que escrevi, a integração testa uma biblioteca com vários programas de teste que lêem os dados e os alimentam na biblioteca, depois verifica os resultados. Então eu faço isso com threads. Então eu faço isso com threads e fork () no meio. Então eu o executo e mato -9 após 2 segundos, inicio e verifico seu modo de recuperação. Eu confuso. Eu o torturo de todos os tipos.

Tudo isso também está sendo testado, mas não tenho uma exibição bonita de vermelho / verde para os resultados. Ou é bem-sucedido ou vasculho alguns milhares de linhas de código de erro para descobrir o porquê.

É aí que você testa o "código real".

E eu pensei nisso, mas talvez você não saiba quando deve escrever testes de unidade. Você terminou de escrever testes de unidade quando seus testes exercitam tudo o que você especificou que deve fazer. Às vezes, você pode perder o controle disso entre todos os casos de erros e manipulação de borda; portanto, convém fazer um bom grupo de testes de testes de caminho feliz que simplesmente seguem as especificações.

Zan Lynx
fonte
(suas = possessivo, é = "é" ou "tem" Ver, por exemplo. Como usar seu e ele é .)
Peter Mortensen
-6

Em resposta ao título da pergunta: "Quando você escreve o código" real "no TDD?", A resposta é: 'quase nunca' ou 'muito lentamente'.

Você parece um estudante, então eu responderei como se estivesse aconselhando um aluno.

Você vai aprender muitas 'teorias' e 'técnicas' de codificação. Eles são ótimos para passar o tempo em cursos para estudantes muito caros, mas de muito pouco benefício para você que você não conseguia ler em um livro na metade do tempo.

O trabalho de um codificador é apenas produzir código. Código que funciona muito bem. É por isso que você, o codificador, planeja o código em sua mente, no papel, em um aplicativo adequado etc., e planeja solucionar possíveis falhas / buracos com antecedência, pensando lógica e lateralmente antes da codificação.

Mas você precisa saber como quebrar seu aplicativo para criar um código decente. Por exemplo, se você não soubesse da Little Bobby Table (xkcd 327), provavelmente não limparia suas entradas antes de trabalhar com o banco de dados, para que não pudesse proteger seus dados com base nesse conceito.

O TDD é apenas um fluxo de trabalho projetado para minimizar os bugs no seu código, criando os testes do que poderia dar errado antes de você codificar seu aplicativo, porque a codificação pode ficar exponencialmente difícil quanto mais código você introduzir e você esquecer os bugs que antes pensava. Depois que você conclui o aplicativo, executa os testes e o boom, esperamos que os erros sejam detectados nos seus testes.

TDD não é - como algumas pessoas acreditam - escrever um teste, passar com código mínimo, escrever outro teste, passar com código mínimo, etc. Em vez disso, é uma maneira de ajudá-lo a codificar com confiança. Esse ideal de código de refatoração contínua para fazer funcionar com testes é idiota, mas é um bom conceito entre os alunos, porque os faz se sentir bem quando adicionam um novo recurso e ainda aprendem a codificar ...

Por favor, não caia nessa armadilha e veja seu papel de codificar para o que é - o trabalho de um codificador é apenas produzir código. Código que funciona muito bem. Agora, lembre-se de que você estará no relógio como codificador profissional, e seu cliente não se importará se você escreveu 100.000 afirmações ou 0. Eles querem apenas um código que funcione. Muito bem, de fato.

user3791372
fonte
3
Não sou nem um aluno próximo, mas leio e tento aplicar boas técnicas e ser profissional. Então, nesse sentido, eu sou um "aluno". Eu apenas faço perguntas muito básicas, porque é assim que eu sou. Eu gosto de saber exatamente por que estou fazendo o que estou fazendo. O coração da matéria. Se não entendi, não gosto e começo a fazer perguntas. Preciso saber por que, se vou usá-lo. O TDD parece intuitivamente bom em alguns aspectos, como saber o que você precisa criar e pensar sobre as coisas, mas a implementação foi difícil de entender. Eu acho que tenho uma compreensão melhor agora.
johnny
4
Essas são as regras do TDD. Você é livre para escrever o código da maneira que quiser, mas se você não seguir essas três regras, não estará fazendo o TDD.
Sean Burton
2
"Regras" de uma pessoa? TDD é uma sugestão para ajudá-lo a codificar, não uma religião. É triste ver tantas pessoas aderindo a uma ideia tão analmente. Até a origem do TDD é controversa.
user3791372
2
@ user3791372 TDD é um processo muito rigoroso e claramente definido. Mesmo que muitos pensem que isso significa apenas "Faça alguns testes quando estiver programando", não é. Vamos tentar não confundir os termos aqui, esta questão é sobre o processo TDD, não sobre testes gerais.
Alex28 /