Categories
Agile Engenharia de software Refactoring Testes

Test infection: por onde começar?

Fala GC!

Cara, o que voce faria se entrasse em um projeto para implementar umas melhorias, mas esse projeto não tivesse nenhum tipo de teste, o código não fosse testável e você soubesse que daqui a 3 meses ia sair do projeto? Você perderia tempo fazendo refactoring, implementando testes, configurando CruiseControl e tal, ou ia simplesmente ligar o foda-se???

Isso acontece pelo menos 32409 vezes todos os dias em algum lugar do planeta.

Os desenvolvedores acostumados a programar com testes têm uma dificuldade enorme de trabalhar em aplicações que não tem testes. A primeira coisa que os desenvolvedores mais experientes fazem quando pegam uma aplicação nova é olhar a base de testes. Por alí já é possível descobrir uma série de comportamentos e detalhes da implementação do sistema, bem como as intenções de quem o programou. Mas o que você faz quando cai de pára-quedas num projeto que não tem um mísero teste sequer? Você não pode deixar esse problema para outra pessoa? É você o infeliz que tem que resolver o problema e fazer a faxina?

Minha resposta para essa pergunta é bem simples: SIM, você é o infeliz que tem que resolver o problema.

<história>
Uma vez eu trabalhava numa empresa e meu chefe me pediu para fazer um protótipo de um sistema que integrava um website com um PABX para fazer determinadas tarefas. Como era um protótipo e eu só tinha duas semanas, eu fiz ZERO testes (e a aplicação funcionava por milagre divino). Só que para o meu azar, esse protótipo era para fazer uma demonstração para um cliente, que adorou a idéia e comprou na mesma hora! Como meu chefe achava que já estava parcialmente pronto e funcionando, meu prazo para terminar era de mais quatro semanas. Duas semanas depois eu estava desesperado por não conseguir avançar na aplicação e não conseguir fazer o negócio funcionar. Aconteciam bugs estranhos, daqueles que te fazem pensar que você está na profissão errada. Todos os dias eu me arrependia profundamente de ter deixado os testes para trás. Meu chefe ficou desesperado e colocou mais um desenvolvedor para me ajudar. No primeiro dia ele me perguntou: “GC, onde estão os testes para eu dar uma olhada?”. Er.. bom… não tinham testes… E eu fiquei totalmente desmoralizado, fui humilhado, massacrado e apanhei quase até a morte.
</história>

Hoje em dia não abro mão dos testes. Se você entregar uma aplicação que não funciona, a culpa é sua! Então, faça tudo que é possível para garantir que tudo esteja funcionando.

Finalmente, respondendo o e-mail do meu amigo acima (que não posso dizer o nome porque não tenho permissão), eis algumas dicas para você não ficar em maus lençóis:

Dica número 1: faça sempre o melhor que puder! Mesmo que você ache que vai ficar 2 semanas ou 3 meses em um projeto, seu gerente pode mudar de idéia e você pode acabar ficando bem mais tempo do que isso. Então, tire essa idéia da cabeça já e comece a se preocupar com os testes. E mesmo que você com certeza absoluta só fique 3 meses, como que você vai sair de cabeça erguida e com a certeza de que o que você fez está funcionando se você não tem os testes para comprovar?

Dica número 2: conserte as janelas quebradas. Se alguém chega na sua casa e metade das janelas estão quebradas, então não tem muito problema se quebrar mais uma. Porém, se todas as janelas estiverem impecáveis, quebrar uma janela é péssimo! Siga este mesmo princípio para o seu código, não tenha janelas quebradas. Se você tiver 90% de cobertura de testes, todos eles forem impecáveis e nenhum falhar no CruiseControl, o próximo desenvolvedor que chegar se sentirá na obrigação de manter tudo funcionando da mesma forma, porque esta é a cultura do projeto. Já se todos os testes estiverem quebrados ou não houverem testes, não faz a menor diferença.

Dica número 3: não abraçe o mundo com as pernas! Não precisa refatorar a aplicação inteira de uma vez, até porque você corre um grande risco de tudo parar de funcionar e você ser demitido, além de que não vai conseguir implementar as features. Neste caso, minha estratégia seria refatorar o código na medida que precisasse implementar as features. Faria o ciclo normal de TDD: implementaria os testes fazendo refactor na implementação para torná-la testável, implementaria a feature, garantiria seu funcionamento e depois um novo refactor para deixar tudo bonito.

Dica número 4: get them Test Infected! Faça um workshop com os outros desenvolvedores do time e mostre para eles a importância de escrever testes. Ensine-os a usar o CruiseControl, JUnit, JMock, injeção de dependência e tudo mais que for necessário para que os testes aconteçam. Mesmo que você “perca” dois ou três dias de trabalho, com certeza ganhará muito mais desse dia em diante.

<piada>
Dica número 5: só por via das dúvidas, para ajudar nos momentos de fraqueza, coloque alguém para te vigiar em tempo integral. O “Dijkstra is watching” é ótimo para isso, tenha ele sempre por perto.
</piada>

Categories
Java TDD

Testes com JUnit + HSQLDB

Hoje fiz uma experiência legal no projeto em que estou trabalhando.

Estou há um tempinho bolando uma forma razoável para testar a aplicação de cabo a rabo. A única coisa que eu ainda não estava convencido totalmente sobre como fazer era testar as classes DAO ou fazer testes envolvendo elas.

Já lí em alguns lugares algumas pessoas falando que os testes não devem utilizar o banco de dados por uma série de motivos e que os DAOs sempre devem ser mocks. Até eu mesmo já cheguei a questionar se isso é realmente necessário. Depois de um tempinho com a idéia fermentando na cabeça eu penso o seguinte:

Primeiro: É interessante testar os DAOs porque você precisa garantir por exemplo que todos os campos estão sendo lidos corretamente para os objetos e que as queries funcionam. No caso de utilização de JPA/Hibernate ainda te ajuda a validar se o mapeamento objeto-relacional está correto.

Segundo: Existem alguns tipos de testes que podem ser feitos na sua aplicação. Se falarmos de teste unitário eu concordo que não deve haver utilização de banco de dados mas sim mocks para os DAOs e tudo mais que não for objeto do teste em questão. Porém para testes de integração ou aceitação é desejável que o banco de dados seja incluído no teste já que faz parte da aplicação.

Mas o problema de envolver o BD nos testes é que eles (os testes) ficam muito lentos e isso desfavorece o desenvolvimento guiado por testes, que eu não vivo sem. Se os testes demoram eles são executados menos vezes e isso não é bom. Sem contar que se os dados do banco forem modificados os testes quebram. Argh!

Eis que surge o HSQLDB. O HSQLDB é um banco de dados escrito em Java puro, tem driver JDBC que funciona direitinho e entende comandos SQL com JOINs, GROUP BYs e praticamente tudo mais que você utiliza numa aplicação que utiliza o banco de dados de forma NORMAL (normal = não utiliza aquelas tosqueiras proprietárias do BD que depois que se usa não dá mais para trocar nem a versão do bicho!).

O que eu fiz foi fazer com que os testes se conectassem num HSQLDB ao invés do banco de dados de desenvolvimento. Para melhorar, esse HSQLDB não está nem rodando em modo server em lugar nenhum. Ele é iniciando junto com os testes e roda em memória na mesma JVM que os testes rodam.

Quais foram os ganhos obtidos com este approach:

1) Minhas queries agora estão sendo testadas, assim como a criação dos meus objetos pelos DAOs.

2) Os testes estão muito rápidos, nem parece que usam banco de dados! Eu tinha criado duas suites de testes, uma que tinha testes com BD e outra sem BD. Isso nem será mais necessário agora.

3) Agora eu tenho a garantia de que os dados de teste não serão modificados e com isso os testes não quebrarão mais por este motivo. Sabendo que os dados não mudarão posso fazer testes mais completos e elaborados.

Porém também experimentei alguns pontos negativos:

1) Qualquer alteração de modelo tem que ser refletida no script de startup do HSQLDB. O script de startup é necessário para indicar quais tabelas com quais campos e dados ele criará em memória. São CREATE TABLEs com duas ou três coisinhas a mais, muda pouca coisa.

2) Em algumas aplicações que eu trabalho tem lógica da query no hint!!! Isso acontece porque lá na empresa tem um trilhão de aplicações rodando em produção no mesmo Oracle que eu rodo minha aplicação. Sendo assim se eu faço uma query ferrada eu derrubo o banco e junto com ele uma boa parte da empresa. Para evitar que isso aconteça os DBAs otimizam uma boa parte das queries que fazemos, só que as vezes a coisa fica tão dramática que os DBAs têm que fazer MAGIAS no hint para a query executar num tempo razoável. Aí eles mexem tanto no hint que no final a query depende do hint para funcionar, quando o hint deveria ser só para ela ficar mais rápida. Aí fica assim: com o hint a query retorna o resultado de um jeito e sem hint de outro! Como hint é coisa do Oracle e o HSQLDB entende como comentário, eventualmente não será possível testar uma ou outra query porque no HSQLDB o resultado será retornado sem executar a lógica contida no raio do hint!

Vou evoluir mais em cima desta idéia e na medida que as coisas forem acontecendo eu posto aqui mais comentários.