Quais são as desvantagens de escrever código antes de escrever testes de unidade?

33

Eu sempre vi a recomendação de que primeiro devemos escrever testes de unidade e depois começar a escrever código. Mas acho que seguir o outro caminho é muito mais confortável (para mim) - escreva o código e faça os testes de unidade, porque sinto que temos muito mais clareza depois de escrever o código real. Se eu escrever o código e depois os testes, talvez seja necessário alterar um pouco o código para torná-lo testável, mesmo se eu me concentrar muito na criação de um design testável. Por outro lado, se eu escrever os testes e depois o código, os testes mudarão com bastante frequência à medida que o código for configurado.

Como vejo muitas recomendações para começar a escrever testes e depois passar para a codificação, quais são as desvantagens se eu fizer o contrário - escrever código e depois os testes de unidade?

k25
fonte
7
+1 para perguntar por que uma determinada prática é uma "melhor prática", antes de abraçá-la
TaylorOtwell

Respostas:

37

Vermelho é a resposta. Vermelho é o que você obtém do ciclo de refator vermelho-verde do TDD que você não pode obter, último teste. Primeiro, escreva um teste com falha. Veja isso falhar. Esse é o seu vermelho e é importante. Ele diz: Eu tenho esse requisito e sei que meu código não está satisfazendo. Portanto, quando você passa para a etapa 2 (verde), sabe, com tanta certeza, que seu código agora está cumprindo esse requisito. Você sabe que alterou sua base de códigos de forma a satisfazer o requisito.

Os requisitos (testes) desenvolvidos após o código, com base no código, privam você desse tipo de certeza, dessa confiança.

Carl Manaster
fonte
+1 - excelente ponto e ponto conquistado! Obrigado pela sua opinião!
K25
7
Testes! = Requisitos. Os testes e o código devem ser derivados dos requisitos.
Bart van Ingen Schenau
2
@ Bart van Ingen Schenau: A força do TDD é precisamente que os testes SÃO os requisitos. Além disso, são requisitos executáveis.
Mouviciel 14/01
1
@Bart: Os testes de unidade geralmente são muito detalhados para os requisitos do cliente (de alto nível), mas a idéia definitivamente se mantém, especialmente se considerarmos também os testes de nível superior, como testes de aceitação automatizados que, uma vez escritos, devem ser requisitos definitivos. Essa é a essência do "teste de aceitação ágil".
Martin Wickman
3
TDD não é sobre teste, é sobre especificação. Os testes construídos em uma abordagem TDD são um meio de comunicação entre desenvolvedor e cliente para concordar com o produto que deve ser feito.
Mouviciel
18

Se você escrever o código e depois os testes, é muito fácil cair na armadilha de escrever os testes para que o código seja aprovado, em vez de escrever os testes para garantir que o código atenda às especificações.

Dito isto, definitivamente não é a única maneira de fazer as coisas, e não há uma "melhor" maneira de desenvolver software. Se você dedica muito trabalho inicial ao desenvolvimento de casos de teste, não sabe se a arquitetura proposta apresenta falhas até muito mais tarde - enquanto se você desenvolveu o código primeiro, você os encontrará mais cedo e poderá redesenhar com menos afundamento. esforço.

Anon.
fonte
Sim, você está certo sobre o primeiro ponto, mas sempre me certifico de não fazer isso. Se o teste falhar, eu sempre vou ao código, certifico-me de que está correto e depois vejo se meu teste está correto e modifico o que estiver errado. Obrigado por sua opinião, eu vou manter isso em minha mente .. +1 de mim para o 1º ponto eo último ponto ...
k25
2
Mas e se o teste for aprovado? O teste pode passar porque não está realmente exercitando o código de interesse; isso realmente não pode acontecer no TDD, porque o teste deve falhar inicialmente, e falha - se não acontecer, você não vai para a etapa 2 até corrigi-lo. Portanto, há um modo de falha no último teste que não existe primeiro no teste.
Carl Carlton
@ Carl Manaster - Sim, você tem um ponto válido. Depois de escrever o código, estou perfeitamente ciente dos requisitos e, portanto, meu caso de teste de unidade estaria correto (idealmente). Se o meu caso de teste for aprovado, eu diria que o código está correto; se o teste falhar, seguirei o que disse. Mas, eu 100% concordo que você tem um ponto válido lá.
K25
@ k25: O ponto é que, se o seu caso de teste for aprovado, você ainda não sabe se o código está correto ou não. O caso de teste pode estar errado.
Anon.
@Anon. - Sim, você está certo, levarei este caso também em consideração.
k25
12

Na verdade, as pessoas que se interessam pelo TDD são testes, apesar de esquecerem as outras duas letras da sigla. Algo que pode ser lido aqui: TDD sem o T ou TDD não tem a ver com Teste .

A coisa é que eu aprendi uma infinidade de outras coisas que estão intimamente ligadas ao TDD. Não importa se você faz o teste primeiro: o que importa é pensar em design de software .

Para ser capaz de escrever testes de unidade "da maneira certa", ou seja, para que sejam isolados, rápidos e automatizados, esperamos que você perceba que é necessário repensar sobre como organizar seu código de maneira que seu código se torne mais fácil testar.

Pessoalmente, aprendi os princípios do SOLID sem saber que havia algo assim escrito. Isso ocorre porque escrever testes de unidade me forçou a reescrever classes para que elas não se tornem muito complexas para testar. Isso levou a coisas como:

  • Eu tive que mudar a funcionalidade, que não fazia sentido ou residia em métodos particulares, para separar classes para que eu pudesse testá-las separadamente. (Princípio da responsabilidade única).
  • Eu tive que evitar grandes estruturas de herança e estender implementações com composição (proeminente no princípio Aberto-Fechado).
  • Eu tinha que ser esperto em relação à herança, usava classes abstratas sempre que via código comum que podia ser compartilhado e usava métodos stub (Princípio de Substituição de Liskov).
  • Eu tive que escrever interfaces e classes abstratas para poder testar as classes em separado. O que inadvertidamente leva você a escrever objetos simulados. (Princípio de segregação de interface)
  • Como escrevi muitas interfaces e classes abstratas, comecei a declarar variáveis ​​e parâmetros para usar o tipo comum (princípio de inversão de dependência).

Mesmo que eu não faça o teste o tempo todo, sigo bons princípios e práticas de OO que você começa a seguir, apenas para facilitar um pouco os testes. Agora não estou escrevendo código por si só. Eu escrevi o código para que ele possa ser facilmente testado ou mais importante; facilmente mantido .

Spoike
fonte
1
O +1 para o SOLID ocorre naturalmente quando você pensa em design de software.
Ocodo
+1 (na verdade, eu queria dar +10, mas não posso). Exatamente meus pensamentos - sua lista de pontos foi extremamente boa. Essa é uma das razões pelas quais fiz essa pergunta. Senti que as aulas se tornaram muito mais quando comecei a escrever os testes de unidade depois de escrever o código. Mas eu queria ver as vantagens / desvantagens de ambos os lados, obrigado por suas opiniões!
K25
10

Todas as outras respostas são boas, mas há um ponto que não foi abordado. Se você escrever o teste primeiro, garante que os testes sejam gravados. Depois de escrever o código de trabalho, é tentador ignorar os testes e apenas verificá-lo através da interface do usuário. Se você tem a disciplina de sempre ter um teste reprovado antes de escrever o código, pode evitar essa armadilha.

RationalGeek
fonte
4

Se você escrever seus testes primeiro, isso lhe dará outra chance de pensar em seu design, antes que ele seja "moldado em pedra".

Por exemplo, você pode pensar que precisa de um método que utilize um determinado conjunto de parâmetros. E se você escrevesse o código primeiro, escreveria dessa maneira e faria o teste se ajustar aos parâmetros especificados. Mas se você escrever o teste primeiro, poderá pensar "espere um minuto, eu não gostaria de usar esse parâmetro no código da linha principal, então talvez eu deva mudar a API".

Anon
fonte
+1 para o primeiro ponto. Mas, não atingindo o nível de parâmetros, e se o projeto fosse discutido com outras pessoas e aceito?
K25
@ k25 - se algo é difícil de usar como projetado, ele precisa de mais reflexão. Às vezes - muito raramente - é apenas uma tarefa difícil. Mais frequentemente, porém, pode ser reduzido a tarefas mais simples. Não tenho um link, mas Gosling ou Goetz fizeram uma entrevista sobre o design da API há alguns anos atrás ... no Google.
Anon
certeza, graças para os ponteiros, eu certamente olhar para eles ...
k25
2

Como vejo muitas recomendações para começar a escrever testes e depois passar para a codificação,

Há uma boa razão para isso.

Se você diz "faça o que parece certo", as pessoas fazem as coisas mais idiotas e loucas.

Se você disser "escreva testes primeiro", pelo menos as pessoas tentarão fazer a coisa certa.

quais são as desvantagens se eu fizer o contrário - escrever código e depois os testes de unidade?

Geralmente, um teste ruim e um design que precisam ser reformulados para serem testados.

No entanto, isso é apenas um "geralmente". Algumas pessoas desenvolvem os projetos e testes em paralelo. Algumas pessoas colocam código testável no lugar e escrevem testes sem retrabalho.

A regra "Primeiro teste" está lá especificamente para ensinar e instruir pessoas que não têm idéia alguma.

De maneira semelhante, somos instruídos a sempre olhar "nos dois sentidos" antes de atravessar a rua. No entanto, na verdade não. E isso não importa. Eu moro em um país com volante à direita e só preciso olhar para a esquerda quando começar a atravessar.

Quando visito um país com volante à esquerda, olhar apenas para a esquerda pode me matar.

As regras são estabelecidas muito fortemente por uma razão.

O que você faz é seu próprio problema.

S.Lott
fonte
2

o ponto de escrever o teste primeiro é que faz você pensar sobre

  • como testar o código
  • a interface que o código deve apresentar para ser testável

se você está fazendo algo simples, provavelmente não importa qual você escreve primeiro (embora seja bom cultivar o hábito de testar primeiro), pois o teste será simples e a interface será óbvia

mas o TDD é escalado para testes de aceitação, não apenas para testes de unidade, e a interface se torna não trivial.

Steven A. Lowe
fonte
1

Primeiro, se você não escrever seus testes primeiro, não estará fazendo o TDD (Test Driven Development). Os benefícios são numerosos e geralmente difíceis de acreditar até que você o pratique várias vezes. Aqui estão os benefícios que recebi ao fazer TDD sobre o desenvolvimento tradicional:

  1. Uma rede de testes de segurança - permite que você faça grandes alterações sem o medo de quebrar algo sem saber
  2. Design orgânico - o design com o qual eu termino geralmente é diferente do design que eu teria feito do zero e sempre foi melhor
  3. Produtividade - trabalhar em direção a objetivos pequenos (passar neste teste) e fazê-lo (todos os testes passarem) funcionam muito bem para mim e me mantém motivado. Adicione um par e minha produtividade alcança novos máximos.

Livros: Beck, K. Desenvolvimento Orientado a Testes por Exemplo

Bom exemplo: http://jamesshore.com/Blog/Lets-Play/

Mike Polen
fonte
+1 - pontos agradáveis ​​(especialmente o 1º) e obrigado pelos links!
k25
0

Ao escrever um teste, como você sabe que ele detectará uma condição de falha? A resposta é "teste o teste". Como você faz isso é escrever o teste primeiro, vê-lo falhar e só passar quando a unidade em teste for codificada com sucesso (o ciclo vermelho / verde / refator mencionado em uma das outras respostas).

Escrever o código primeiro e depois o teste deixa em aberto a questão de se o teste mostraria uma falha honesta.

Lembre-se de que seus testes expressam especificações. Se você precisar revisar seus testes à medida que seu código "se molda", isso sugere que suas especificações estão mudando. Isso pode ou não ser uma coisa boa. Isso poderia significar que sua compreensão do problema não estava inicialmente correta. Por outro lado, isso pode significar que você está testando "como" a unidade está fazendo seu trabalho, e não o que deveria estar realizando.

Zenilogix
fonte