Olá galera, tudo certin? Esse post é uma confissão.
Sim, resolvi começar esse post confessando minha arrogância – eu não escrevo testes sempre (Segundo o Giovanni Bassi, por isso, não deveria me considerar um dev sênior, e tenho coragem de dizer que sou arquiteto – que vergonha!);
Mas a questão é: Por quê? Por que eu não escrevo testes sempre? …
- Se eu sei que testes impedem que eu libere besteira;
- Se eu sei que testes me dão mais segurança para fazer a manutenção;
- Se os testes permitem que eu possa ganhar tempo, não pagando o tempo de start da aplicação em debug;
- Se eu sei que o teste é replicável;
- Se eu experimento o benefício da automação dos testes todos os dias..
Por que eu não escrevo testes sempre? (Raiva e indignação comigo mesmo)
- Sou ingênuo? acho que não [pelo menos, não é a palavra que melhor me descreve];
- Gosto de perder tempo? definitivamente não;
- Gosto de passar vergonha por liberar código bugado? Nem respondo…
Por que não escrevo testes (a questão persiste)… Resposta mais aceitável, sou arrogante e caio na falácia do código simples.
O que eu quero dizer por código simples?
Resumindo:
- poucas linhas;
- pouco ou nenhum desvio condicional;
- pouco ou nenhum loop;
- nome do método é óbvio e significado do código está claro.
O que o “bonzão” aqui pensa quando vê um código desses? Para que testar? Imagine. Não tenho como errar em um código desses. (Eu sou fo6@!).
Qual é o impacto desse tipo de código?
Enorme!! Pelo menos no meu caso, o código “simples” é geralmente utilizado para suportar fluência e assemelhados. Ou, também, algumas funções utilitárias. Funções de conversão.. Funções de validação.. Essas coisinhas que usamos por toda a parte.
Logo, se um código desses saí com erro, o que acontece? Todo o sistema mostra comportamentos estranhos! Pior, gasta-se um tempo enorme tentando encontrar o erro nas funções cliente. Depuração passo-a-passo, obviamente com skip nas funções de código “simples” (por que erraríamos nelas?)
Se o código é tão “simples”, por que sai errado?
Duas respostas diretas:
- copy and paste – sim, aqui a violação de outro princípio, DRY. Mas, considere o cenário de um método com em três linhas, todas particulares do método, mas, mesmo assim, semelhantes a outro .. a tentação de pegar outro método e só modificar o necessário é grande … e … esquecemos umas coisinhas. Pior, pegamos código bugado e replicamos o bug .. criando uma sementinha do mal;
- arrogância – eu confesso, não testo código que considero simples, nem em execução. Espero para fazer testes maiores (integrando). Definitivamente não considero um código simples uma SUT respeitável. E … bem .. já sabemos o final da história.
Qual é o antídoto?
Resposta direta, reconhecer a arrogância e assumir que “pequenas merd!#@$ fazem grandes cag#$as”¨. Não vejo outra saída: Escreva testes e tenha uma consciência tranqüíla.
Meu SUT de exemplo… A classe Times!
Essa classe já é nossa conhecida. Ela foi o tema central de outro post (que vergonha!! Classe bugada) aqui do blog: trata-se d
Essa classe é, por essência, código simples. Repare que todos os métodos são curtinhos… Olhe a lógica dos métodos Or/And/Not e relacionados .. tão parecidos .. tanto que eu copiei e colei .. alterei só o necessário .. uma classe assim não deve precisar de testes .. certo? ERRADO!
Vou mostrar os erros que o código tem e os testes que eu deveria ter escrito…
Método AtLeast
Esse foi minha primeira lição. Se eu tivesse escrevido esse teste:
Teria percebido que meu método está errado. AtLeast_EqualsToMin_ReturnsTrue falha vergonhosamente. O que eu errei, faltou um “igualzinho” no método … droga!
Uma linha de código e um erro! Faltou um teste!
Outra pancada da cabeça … método Or
Essa doeu mais… O método Or foi o primeiro “booleano” que escrevi .. me empolguei e escrevi os demais na base do Copy and Paste. Se eu tivesse, pelo menos, escrito o teste .. só dele .. não teria replicado um bug. Bastava esse teste para evidenciar minha distração (perdoável):
Mas não escrevi o teste (isso sim não é deplorável), e publiquei uma versão com falhas. Simplesmente, não usei o parâmetro que eu mesmo defini (variável e2) .. Aqui a versão corrigida:
Pior .. repliquei a falta.
Além disso…
Toda a minha classe não implementa nada de programação defensiva… que vergonha! E eu ainda falo de Contratos .. Coisa Feia!
Seguindo minha metodologia de testes: escrever testes para cenários válidos, outros para cenários inválidos (tipo: testar reação para parâmetros fora de range, ou nulos, teria identificado o “problema”).
Concluindo
Me permiti trair por minha própria arrogância … Liberei código bugado para a comunidade por não ter tido a humildade de reconhecer que todo código precisa ser testado. Mesmo os códigos simples precisam ser verificados .. Caí na falácia do código evidente, e tenho que reconhecer publicamente minha incompetência …
Lição de hoje: Caros, partindo de hoje, testo tudo!
Por hoje, é isso
Glauco Vinicius
31/08/2010
Elemar,
Muito bom seu post.
A única coisa que não me deixou feliz foi o último teste com mais de um assert.
Eu particularmente prefiro a abordagem de “One assert per test”: http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html
=)
elemarjr
02/09/2010
Conheço o conceito, mas, honestamente, acho que ainda não devo ter a maturidade necessária para reconhecer esse princípio como válido sempre.
Obrigado pela dica do blog.
Edmilson
31/08/2010
Elemar, Excelente Post!!
[]´s
Edmilson
Felipe
31/08/2010
Elemar, mais uma vez: Parabéns!
Denis Ferrari
31/08/2010
Parabéns Elemar! É ótimo ver exemplos onde usar/não usar testes fazem a diferença! Show de bola o post!
Paulo Silveira
03/09/2010
parabens, excelente post! é igual quando a gente vai mostrar TDD pra alguem e vai fazendo os testes antes, eles falham. ai voce acha que mexeu o suficiente pra arrumar, e ele continua falhando… mostra que mesmo planejado e ensaiado, voce pode errar.
Michel Alves
08/01/2011
Gostei do post Elemar, para quem está começando tira algumas caraminho-las da cabeça. Vou recomendar para uns amigos.