terça-feira, 18 de dezembro de 2007

TDD - Auxiliando a desenvolver consultas com Hibernate + Spring

Quando usamos sql "puro", testar uma consulta é muito simples de ser feito, pois podemos utilizar uma ferramenta de BD como o MySQL Admin, ou PGAdmin, porém quando usamos APIs de Persistência, o problema aparece.

Esses dias, num caso de uso que necessitava de uma consulta nem tão complexa, tive que startar e re-startar o servidor de aplicação 8 vezes, para testar a consulta que era montada.

Daí, fiquei pensando se TDD poderia me ajudar, e ví que ele pode me ajudar e muito.

Usando TDD para o desenvolvimento de uma consulta, além de diminuir o tempo perdido com o servidor de aplicação sendo levantado e derrubado várias vezes, ainda ganho o tempo de abrir a aplicação, preencher campos (como um filtro por exemplo) e submeter um formulário.

Para fazermos TDD com consultas, precisamos gerenciar as transações para garantir que determinadas informações estarão na base de dados e outras não sejam persistidas.

Utilizando Hibernate + Spring precisamos extender a classe AbstractTransactionalSpringContextTests do Spring, para gerenciar as transações.

extends AbstractTransactionalSpringContextTests

No método de testes devemos escrever o método getConfigLocations para carregar os arquivos de contexto do Spring:
0: @Override
1: protected String[] getConfigLocations()
2: {
3: setAutowireMode(AUTOWIRE_BY_NAME);
4: return new String [] {"applicationContext*.xml"};
5: }
No método de teste, você insere registros para simular um banco de dados populado:
 0: public void testGetAvisosMesAno()
1: {
2: Aviso a1 = new Aviso();
3: a1.setId(1L);
4: a1.setData(new Date(107, 11, 1));
5:
6: Aviso a2 = new Aviso();
7: a2.setId(2L);
8: a2.setData(new Date(107, 10, 1));
9:
10: Aviso a3 = new Aviso();
11: a3.setId(3L);
12: a3.setData(new Date(107, 11, 20));
13:
14: avisoDao.save(a1);
15: avisoDao.save(a2);
16: avisoDao.save(a3);
17:
18: Collection avisos = avisoDao.
19: getAvisosMesAno(new Date(107, 11, 1),
20: new Date(107, 11, 30));
21: assertEquals(2, avisos.size());
22: }
Se já existirem dados no banco, o tamanho do retorno (avisos.size()) não é um bom teste, é melhor verificar se os elemento que deseja, estão lá, e se os que não deseja, não estão.


A lição que deve ser deixada é que TDD é tão ou mais útil para um método de persistência do que para um método de negócio.
O "ou mais" é o que acho que se encaixa melhor, pois imagine a possibilidade de errar na criação de uma consulta, além de tudo, usamos muito "palavras" que não são compiladas, como o nome de campos da sua classe.

Outra dica é que mesmo usando SQL puro, o TDD pode conduzir muito bem a construção da sua query, e acho que mesmo sendo fácil testar a consulta com outras ferramentas, o TDD pode trazer mais benefícios do que, simplesmente, testar a consulta.
Veja também o post sobre 100% de Cobertura.