TDD: Estou fazendo certo?

14

Sou um novo programador (só aprendo há cerca de um ano) e, no meu objetivo de melhorar, aprendi recentemente sobre o TDD. Eu queria adquirir o hábito de usá-lo, pois parece muito útil. Eu queria verificar e ter certeza de que estou usando corretamente.

O que eu estou fazendo:

  1. Pense em um novo método que eu preciso.
  2. Crie um teste para esse método.
  3. Falhar no teste.
  4. Escreva método.
  5. Teste de passagem.
  6. Refatorar método.
  7. Repetir.

Estou fazendo isso para todo método que escrevo, há alguns que não devo me preocupar? Mais tarde, costumo pensar em uma maneira de testar meus métodos já existentes de uma maneira ou situação diferente. Devo fazer esses novos testes, ou como cada método já possui um teste próprio, não devo me preocupar? Posso testar o meu código? Acho que é a minha principal preocupação ao fazer isso.

EDITAR

Além disso, isso era algo que eu estava pensando. Ao fazer algo como criar uma GUI, o TDD seria necessário nessa situação? Pessoalmente, não consigo pensar em como escreveria testes para isso.

cgasser
fonte
5
Você já está fazendo isso muito melhor do que profissionais experientes que dizem que estão testando tudo (mas não o fazem).
Yannis 18/05/12
O que você descreve não é o espírito do TDD.
1
Você pode procurar no ATDD ou no BDD.
Dietbuddha 20/05
Talvez comece mais alto - pense em um novo módulo que você precisa.

Respostas:

16

O que você está descrevendo como um fluxo de trabalho não é, na minha opinião, o Spirit of TDD.

A sinopse do livro de Kent Becks na Amazon diz:

Simplesmente, o desenvolvimento orientado a testes visa eliminar o medo no desenvolvimento de aplicativos.Embora um pouco de medo seja saudável (geralmente visto como uma consciência que diz aos programadores que "tomem cuidado!"), O autor acredita que os subprodutos do medo incluem programadores tentativos, mal-humorados e não comunicativos que são incapazes de absorver críticas construtivas. Quando as equipes de programação compram no TDD, elas imediatamente veem resultados positivos. Eles eliminam o medo envolvido em seus trabalhos e estão mais bem equipados para enfrentar os difíceis desafios que eles enfrentam. O TDD elimina traços experimentais, ensina os programadores a se comunicar e incentiva os membros da equipe a buscar críticas. No entanto, mesmo o autor admite que o mal-humorado deve ser resolvido individualmente! Em suma, a premissa por trás do TDD é que o código deve ser continuamente testado e refatorado.

TDD prático

Teste formal automatizado, especialmente Teste de unidade, todos os métodos de todas as classes são tão antipadrão quanto não testam nada. Há um equilíbrio a ser tido. Você está escrevendo testes de unidade para todos os setXXX/getXXXmétodos, eles também são métodos!

Os testes também podem ajudar a economizar tempo e dinheiro, mas não esqueça que eles custam tempo e dinheiro para serem desenvolvidos e são código, portanto, eles custam tempo e dinheiro para serem mantidos. Se atrofiam por falta de manutenção, tornam-se um passivo mais que um benefício.

Como tudo isso, existe um equilíbrio que não pode ser definido por ninguém além de você. Qualquer dogma de qualquer maneira é provavelmente mais errado do que correto.

Uma boa métrica é um código que é crítico para a lógica de negócios e sujeito a modificações frequentes com base nos requisitos variáveis. Essas coisas precisam de testes formais automatizados, que seriam um grande retorno do investimento.

Você vai ser muito pressionado a encontrar muitas lojas profissionais que funcionam dessa maneira também. Simplesmente não faz sentido para os negócios gastar dinheiro testando coisas que, para todos os efeitos práticos, nunca mudam após a realização de um simples teste de fumaça. Escrever testes de unidade automatizados formais para .getXXX/.setXXXmétodos é um excelente exemplo disso, completa perda de tempo.

Agora faz duas décadas desde que foi apontado que o teste do programa pode demonstrar de forma convincente a presença de bugs, mas nunca pode demonstrar sua ausência. Depois de citar essa observação bem divulgada com devoção, o engenheiro de software volta à ordem do dia e continua a refinar suas estratégias de teste, assim como o alquimista de outrora, que continuou a refinar suas purificações crisocósmicas.

- Edsger W. Djikstra . (Escrito em 1988, agora está mais perto de 4,5 décadas.)

Veja também esta resposta .

Comunidade
fonte
1
Isso aborda praticamente o que eu estava preocupado. Eu estava sentindo que não deveria estar testando todos os métodos como estava, mas não tinha certeza. Parece que ainda preciso ler mais sobre o TDD.
Cgasser
@kevincline Na maioria das vezes setXXX/getXXXnão são necessários em todos os :)
Chip
1
Quando você memorizar aquele getXXX trivial e errar, ou introduzir um carregamento lento no seu getXXX e errar, você saberá que às vezes você realmente deseja testar seus getters.
Frank Shearar
13

Você está muito perto. Tente pensar desta maneira um pouco diferente.

  1. Pense em um novo comportamento que eu preciso.
  2. Crie um teste para esse comportamento.
  3. Falhar no teste.
  4. Escreva novo ou estenda o método existente.
  5. Teste de passagem.
  6. Código de refatoração.
  7. Repetir.

Não crie getters e setters automaticamente para cada propriedade . Não pense em um método inteiro e escreva os testes para cobrir todas as funcionalidades . Tente encapsular as propriedades dentro da classe e escreva métodos para fornecer o comportamento necessário. Deixe seus métodos evoluírem para um bom design, em vez de tentar planejá-los com antecedência. Lembre-se de que o TDD é um processo de design, não um processo de teste. A vantagem que ele tem sobre outros processos de design é deixar para trás um fluxo de testes de regressão automatizados, em vez de um pedaço de papel que você joga na lixeira.

Lembre-se também das três regras de TDD do tio Bob .

  1. Você não tem permissão para escrever nenhum código de produção, a menos que seja aprovado no teste de unidade.
  2. Você não tem permissão para escrever mais testes de unidade que o suficiente para falhar; e falhas de compilação são falhas.
  3. Você não tem permissão para escrever mais código de produção que o suficiente para passar no único teste de unidade com falha.
pdr
fonte
1
@Zexanima: Você está se saindo muito melhor do que a maioria de nós depois de um ano. Apenas tentando apontar para o próximo passo.
Pd
2
Eu acho que essas três regras às quais você vincula; por mais idílico que pareça, são excepcionalmente dogmáticos e altamente irrealisticamente rígidos em 99% de todas as lojas de produção que alguém encontrará.
1
@FrankShearar ou pode ser visto como a prática impraticável de um extremista fundamentalista e atacadista desconsiderado. Eu trabalhei em lojas que tinham essa atitude dogmática, eles pegaram o dogma literalmente e erraram o alvo; escrevendo testes que não testavam seu código real de uma maneira prática e acabando apenas testando a capacidade das estruturas de Mocking e Injection de Dependência de confundir o que era importante, na melhor das hipóteses.
1
@pdr O espírito de algo é diametralmente oposto à canonização dogmática formalizada dessa coisa. Uma coisa é ter uma filosofia e outra é transformá-la em uma religião . O TDD é mais do que discutido em termos religiosos dogmáticos em preto e branco . Essas três regras parecem dogmáticas e religiosas na apresentação e o que é ouvido é o mantra Teste, Teste, Teste , para alguém como o OP, eles as tomam literalmente e isso causa mais mal do que bem. Afirmei a Frank que afirmações polarizadoras podem fazer mais mal à causa do que bem.
2
Meu argumento era que o dogmatismo vem da aceitação cega de algo como evangelho . Pegue a declaração de polarização, experimente, faça com que você saia da sua zona de conforto. Você não pode avaliar as compensações envolvidas no TDD se não tentar a abordagem extrema de 3 pontos ou tudo ou nada, porque você não terá dados .
Frank Shearar
5

Poucas coisas a serem adicionadas às respostas de outras pessoas:

  1. Existe um excesso de teste. Você deseja garantir que seus testes de unidade se sobreponham o mínimo possível. Não faz sentido que vários testes verifiquem as mesmas condições no mesmo trecho de código. Por outro lado, quando você refatorar seu código de produção e tiver muitos testes que se sobrepõem a essa seção, precisará voltar e corrigir todos esses testes. Enquanto que, se não se sobrepuserem, uma alteração interromperá no máximo apenas um teste.

  2. Só porque você pensou em uma maneira melhor de escrever um teste, eu não voltaria lá e começaria a reescrevê-lo. Isso remonta aos indivíduos que continuam escrevendo e reescrevendo a mesma classe / função, porque tentam aperfeiçoá-lo. Nunca será perfeito, então siga em frente. Quando você descobrir um método melhor, mantenha-o em mente (ou adicione aos comentários do teste). Da próxima vez que estiver lá, e você perceber o benefício imediato de mudar para o novo caminho, é hora de refatorar. Caso contrário, se o recurso estiver pronto e você seguir em frente e tudo funcionar, deixe-o funcionando.

  3. O TDD se concentra em fornecer valor imediato, não apenas em garantir que todas as funções sejam testáveis. Quando você adiciona funcionalidade, comece perguntando "do que o cliente precisa". Em seguida, defina uma interface para fornecer ao cliente o que ele precisa. Em seguida, implemente o que for necessário para fazer o teste passar. O TDD é quase como testar cenários de casos de uso (incluindo todos os "what-ifs"), em vez de simplesmente codificar funções públicas e testar cada uma.

  4. Você perguntou sobre o teste do código da GUI. Procure os padrões "Humble Dialog" e "MVVM". A idéia por trás de ambas é que você crie um conjunto de classes "modelo de exibição", que na verdade não possuem lógica específica da interface do usuário. No entanto, essas classes terão toda a lógica de negócios que normalmente faz parte da sua interface do usuário e essas classes devem ser 100% testáveis. O que resta é um shell de interface do usuário muito fino e, sim, normalmente esse shell é deixado sem cobertura de teste, mas nesse momento ele deve ter quase nenhuma lógica.

  5. Se você possui uma grande parte do código existente, como sugeriram alguns outros, não deve começar a adicionar testes de unidade absolutamente em todos os lugares. Levará uma eternidade e você não se beneficiará da adição de testes de unidade a 80% das classes que são estáveis ​​e não serão alteradas no futuro próximo (ou não tão próximo). No entanto, para novos trabalhos, acho que o uso do desenvolvimento TDD com o código ALL é extremamente benéfico. Você não apenas termina com um conjunto com testes automatizados quando termina, mas o desenvolvimento real tem enormes benefícios:

    • Considerando a testabilidade, você escreverá um código menos acoplado e mais modular
    • Ao considerar seu contrato público antes de mais nada, você terminará com interfaces públicas muito mais limpas
    • Enquanto você escreve código, a verificação de novas funcionalidades leva milissegundos em comparação com a execução de todo o aplicativo e a tentativa de forçar a execução no caminho certo. Minha equipe ainda libera código de tratamento de erros que nem sequer foi executado uma vez só porque eles não conseguiram obter o conjunto certo de condições. É incrível quanto tempo perdemos quando, mais tarde, no controle de qualidade, essas condições acontecem. E sim, muito desse código é o que alguém consideraria "não uma área para muitas mudanças no futuro depois que o teste de fumaça for concluído".
DXM
fonte
1

Existem alguns métodos que não estão sendo testados, a saber, esses testes. No entanto, há algo a ser dito para alguns testes serem adicionados após a gravação do código inicial, como condições de contorno e outros valores, para que possa haver vários testes em um único método.

Embora você possa testar demais o seu código, isso geralmente acontece quando alguém deseja testar todas as permutações possíveis de entradas que não soem exatamente como o que você está fazendo. Por exemplo, se você possui um método que utiliza um caractere, escreve um teste para todos os valores possíveis que podem ser inseridos? É aí que você chegará ao super teste, IMO.

JB King
fonte
Ah! OK. Não é isso que estou fazendo. Normalmente, acabo pensando em uma situação diferente. Poderia testar meus métodos de maneira mais lenta depois de já ter feito o teste inicial. Eu estava apenas certificando-me de que esses testes 'extras' valiam a pena ser feitos, ou se acabou.
Cgasser 18/05/12
Se você trabalha em incrementos suficientemente pequenos, geralmente pode ter certeza razoável de que seu teste realmente funciona. Em outras palavras, ter uma falha no teste (pelo motivo certo!) É, por si só, testando o teste. Mas esse nível de "razoavelmente certo" não será tão alto quanto o código em teste.
Frank Shearar
1

Geralmente você está fazendo certo.

Testes são código. Portanto, se você pode melhorar o teste, vá em frente e refatorá-lo. Se você acha que um teste pode ser aprimorado, vá em frente e mude. Não tenha medo de substituir um teste por um melhor.

Eu recomendo ao testar seu código, evite especificar como o código deve fazer o que está fazendo. Os testes devem observar os resultados dos métodos. Isso ajudará na refatoração. Alguns métodos não precisam ser testados explicitamente (por exemplo, getters e setters simples) porque você os utilizará para verificar os resultados de outros testes.

Schleis
fonte
Eu estava escrevendo testes para getters e setters também, então obrigado por essa dica. Isso vai me poupar algum trabalho desnecessário.
Cgasser 18/05/12
"Alguns métodos não precisam ser testados explicitamente (por exemplo, getters e setters simples)" - Você nunca copiou / colou um getter e setter e esqueceu de alterar o nome do campo por trás dele? O problema do código simples é que ele requer testes simples - quanto tempo você realmente está economizando?
pdr
Não quero dizer que o método não seja testado. É verificado apenas através da confirmação de que outros métodos foram definidos ou durante a configuração real de um teste. Se o getter ou setter não funcionar corretamente, o teste falhará porque as propriedades não foram definidas corretamente. Você os testa gratuitamente, implicitamente.
Schleis
Os testes getter e setter não demoram muito, então provavelmente continuarei a fazê-los. No entanto, nunca copio e colo nenhum código, para não ter esse problema.
Cgasser 18/05/12
0

Minha opinião sobre o TDD é que as ferramentas criaram um mundo de desenvolvedores de estilo 'aponte e clique'. Só porque as ferramentas criam um esboço de teste para cada método não significa que você deve escrever testes para cada método. Algumas pessoas estão 'renomeando' o TDD como BDD (desenvolvimento orientado ao comportamento), onde os testes são muito mais detalhados e pretendem testar o comportamento da classe, e não cada método complicado.

Se você projeta seus testes para testar a classe como se destina a ser usada, começa a obter alguns benefícios, especialmente quando começa a escrever testes que exercem um pouco mais do que cada método, especialmente quando você começa a testar a interação daqueles métodos. Suponho que você possa pensar nisso como escrever testes para uma classe, em vez de métodos. De qualquer forma, você ainda deve escrever 'testes de aceitação' que exercitem a combinação de métodos para garantir que não haja contradições ou conflitos na maneira como são usados ​​juntos.

Não confunda TDD com teste - não é. O TDD foi projetado para que você escreva código para exercitar seus requisitos, não para testar os métodos. É um ponto sutil, mas importante, que muitas vezes se perde nas pessoas que escrevem cegamente o código de teste para todos os métodos. É que você deve escrever testes para garantir que seu código faça o que você quer que ele faça, não que o código que você escreveu funcione como deveria.

Existem alguns bons links à direita sobre o BDD x TDD. Vê-los.

gbjbaanb
fonte
0

Ao começar a aprender TDD, sim, você deve seguir cegamente a abordagem dogmática de não escrever uma única linha de código, exceto para fazer um teste com falha, e escrever apenas um teste suficiente para falhar (e falhar pelo motivo certo / esperado) .

Depois de aprender o que é o TDD, ENTÃO você pode decidir que certos tipos de coisas não valem a pena testar. Esta é a mesma abordagem que você deve seguir para tudo, e as artes marciais japonesas chamam isso de " shuhari ". (O link também explica como se pode progredir nos estágios de aprendizado sem um professor, que é, suspeito, como a maioria das pessoas precisa aprender.)

Frank Shearar
fonte
0

Eu acredito que você está testando demais.

Pratico TDD há muitos anos e, na minha experiência, quando o TDD é realizado de maneira eficaz, você obtém dois benefícios principais:

  • Fornecer feedback rápido
  • Ativar refatoração

Fornecer feedback rápido

Particularmente em linguagens dinâmicas, eu posso executar os testes relevantes em menos de um segundo. E eu tenho observadores do sistema de arquivos executando esses testes automaticamente quando um arquivo de origem é alterado no disco. Portanto, praticamente não tenho tempo de espera para testes e sei imediatamente se o código que escrevi foi o esperado. Assim, o TDD leva a uma maneira muito eficiente de trabalhar.

Ativar refatoração

Se você possui um bom conjunto de testes, pode refatorar com segurança, à medida que obtém novas idéias sobre como o sistema deve ser projetado.

Um bom conjunto de testes permite que você mova a responsabilidade pelo código e ainda tenha confiança de que o código funcionará conforme o esperado após a movimentação. E você deve conseguir fazer isso com pequenas alterações no código de teste.

Se você escrever testes para todos os métodos do seu sistema, é provável que você não consiga refatorar facilmente seu código, pois cada refator de código exigirá grandes alterações no código de teste. E você pode ter certeza de que o código de teste ainda funciona conforme o esperado? Ou você introduziu acidentalmente um erro no código de teste, o que consequentemente leva a um erro no código de produção?

Se você, no entanto, como também sugerido na resposta da pdr , concentre-se no comportamento em vez de nos métodos ao escrever testes, você terá testes que exigirão muito menos alterações ao refatorar o sistema.

Ou, como Ian Cooper diz nesta apresentação (citei de memória, talvez não seja corretamente citado):

Seu motivo para escrever um novo teste deve ser adicionar um novo comportamento, não adicionar uma nova classe

Pete
fonte
-2

Você deve testar todos os métodos públicos .

O problema aqui é que, se seus métodos públicos são muito pequenos, você provavelmente está expondo muita informação. A prática comum de expor todas as propriedades como getXXX()realmente quebra o encapsulamento.

Se seus métodos públicos são realmente o comportamento da classe, você deve testá-los. Caso contrário, eles não são bons métodos públicos.

EDIT: a resposta do pdr é muito mais completa que a minha.

Lasca
fonte