Olá, pessoal! No artigo de hoje eu vou apresentar alguns princípios importantes sobre TDD – tudo com base no conhecimento e experiência que venho adquirindo desde de 2010. Espero que gostem e que o artigo ajude vocês!
No TDD, você desenvolve os testes do software antes mesmo de desenvolver o software (eu chamo de code core). Para cada “peça” da aplicação que é construída, uma série de testes são escritos ANTES do desenvolvimento para garantir que a aplicação funcione como deveria.
Na pratica, quando você termina os testes, terminou o desenvolvimento principal e está seguro que aquela classe faz o que se espera dela – claro que terá que fazer teste de integração, mas aí é outra historia.
A crença do iniciante
Todo mundo que inicia com TDD, no inicio fala:
“Meu Deus, que coisa mais chata e ainda dá mais trabalho. Pra quê eu vou criar uma nova classe com métodos, se eu poderia fazer um método único main, com todos os possíveis erros e ver no console o resultado com System.out.println!?”.
Bem, eu devo confessar que pensei assim também, pois tive resistência ao uso TDD quando fomos apresentados. Um dos pontos era esse: a impressão de que ele dá mais trabalho, por escrever os métodos testes (unit tests). Isso era algo que eu não conseguia entender.
O segundo ponto era pensar nos testes e no fato deles serem específicos para cada situação. Isso era outro ponto difícil, pois não conseguia pensar que tipo de case criar, uma vez que não tinha o código core como done.
Superação
Com persistência e coragem, eu fui me adaptando ao “novo ambiente”. É normal termos resistência ao que é novo e o que me motivo a superá-la foi o fato do mercado usa o TDD e os experts (Martin Fowler, Kent Beck, Guilherme Silveira, Rodrigo Yoshima, etc) de TI defendem o uso. Então, por que eu não devo usar? Será que eles também não tiveram os mesmos problemas no começo? E a partir disso eu fui em frente e virei um viciado em TDD. Não consigo mais fazer uma aplicação, por mais simples que seja, sem TDD. É muito bom ver o sinal “vermelho” e o “verde” – um dizendo que preciso consertar alguma coisa e outro dizendo que o meu o meu conserto foi aprovado.
Antes de TDD
Antes de TDD, eu achava que entregava um programa da maneira como ele tinha sido solicitado, mas não durava muito tempo e o pessoal de QA me mandava todos os bugs que apareceram – e a maioria era em algo que não implementei, ou que achei que estaria funcionando corretamente, mas na verdade não estava. Um exemplo prático que me lembro é que o usuário esperava que uma determinada ação ia retornar o CPF – e eu acreditei que o método retornaria de fato -, mas na verdade, vinha um null e quando fui ver, eu tinha esquecido em algum lugar de passar o valor de um atributo do objeto para o método responsável.
Com o TDD, isso ainda pode acontecer. A diferença é que o risco diminui. Quando entregamos algo com TDD, estamos seguros de que estamos entregando a coisa certa com o risco menor. O problema na minha época pré-TDD é que o tempo de manutenção não era tão curto como esperado, pois eu não sabia onde estava o erro e tinha que usar o debug do eclipse até encontrar. Li em algum lugar que o “debug” deveria ser removido do Eclipse.
Os princípios fundamentais que devemos ter ao adotar TDD
- escrever o TESTE da implementação ANTES de escrever code core;
- escrever apenas o código suficiente para o teste e se contentar com isso (controle sua ansiedade);
- escrever teste pequenos, testando a menor quantidade possível de código de cada vez;
- escrever testes rápidos (não é programar rápido e sim testes que rodem rápido, em milisegundos).
O ciclo de vida TDD
- criar o teste;
- executar todos os possíveis testes e ver a aplicação falhar (barra vermelha no junit);
- escrever a aplicação a ser testada;
- executar os testes para ver se todos passarão;
- refatorar – refactoring;
- executar os testes novamente e garantir que eles continuem passando (princípio de refactoring).
Por que adotar?
- necessidade de manter compatibilidade retroativa;
- baixa cobertura de testes unitários;
- design pouco testável.
Tipos de testes
- Testes unitários – testam apenas um componente do sistema:
- ferramentas: Junit, Jmock
- fundamental para prática do TDD
- Testes de integração – testam integração entre componentes que envolvem dois ou mais. Ex.: classes + SGBD
- ferramentas: Junit, DBUnit
- Teste de aceitação – testam uma funcionalidade, ou caso de uso e envolve vários componentes do sistema.
- ferramentas: Junit, Selenium – pode ser usado em TDD
Consequências
- testes podem ser feito na IDE;
- não há necessidade de fazer um deploy da aplicação para execução dos testes;
- bugs são encontrados com mais facilidade e corrigidos com maior velocidade;
- bugs comprovados por testes unitários (barra vermelha na tela);
- código mais estável, pois estimula um melhor design;
- facilita a técnica de refactoring;
- evitar o “overdesign”, ou seja, só escrever código suficiente para o teste passar.
Pontos de reflexão
Há pessoas que são contra ao uso de TDD, Agile etc. Dizem que isso é coisa de “pensadores” e que não existe “bala de prata”. Mas eu não sei de onde vem o “bala de prata” e fico, às vezes, assustado como essas pessoas não sabem fazer interpretação. Não está escrito em lugar nenhum que o TDD, Scrum, ou XP são bala de prata. Porém, eles deduzem isso. Hoje, particularmente, não discuto mais sobre o assunto com essas pessoas; simplesmente ignoro a discussão (e não a pessoa).
Mas, por que faço isso? Antes (não foi há muito tempo), eu até tentava discutir (normal entre profissionais de TI) e mostrar que não é isso que os Agilistas têm em mente. Mas era difícil e tentar mudar as pessoas é algo praticamente impossível. E um dos princípios que incorporei como Agilista foi “aprendizado” e tive que aprender que não podemos mudar as pessoas. Depois disso, vivo mais feliz, com menos discussões que não vão dar em lugar nenhum.
Quem desenvolve com TDD sabe que é possível desenvolver com zero bugs ou algo próximo disso; e quando um bug é encontrado, o custo de manutenção é relativamente baixo com relação ao que já conhecemos no dia-dia. Porém, você vai encontrar alguém dizendo por ai: “bugs fazem parte do desenvolvimento de sistemas; todo sistema tem bugs”. De novo: não estamos dizendo que um sistema não tem bug. O problema maior não é ter, e sim como resolver e o quanto custa consertar um bug.
E qual o nível desse bug? Você já precisou parar um sprint por causa de bugs? Então, você tem um problema sério na qualidade de desenvolvimento do seu produto, pois ele bloqueou todo o ciclo do produto.
Claro que estou falando de TDD a nível de código, pois é onde temos o uso da pratica mais desenvolvido e com cases e mais cases de diferentes companhias.
Conclusões
- colabora para o aumento da qualidade dos sistemas;
- o software cresce de forma ordenada e com qualidade de design;
- o software se adapta com mais facilidade a mudanças.