1. CENÁRIO ATUAL
Atualmente, testes unitários envolvendo banco de dados dentro de um ambiente Spring podem ser um problema. O framework apresenta uma característica para a execução de testes que tem lados positivos e negativos: ele mantém o seu contexto em cache para testes que utilizam os mesmos arquivos de configuração. Caso um teste utilize arquivos diferentes dos utilizados pelo anterior, o framework criará um novo contexto. Uma vantagem imediata que conseguimos visualizar desse comportamento é a diminuição do tempo total de execução da suíte de testes (testes com ambiente Spring são mais lentos). Entretanto, às vezes o framework acaba armazenando lixo no seu contexto (inclusive dados e estruturas do banco), e isso pode prejudicar a execução de testes posteriores.
O DBUnit é uma extensão do framework JUnit que nos possibilita colocar o banco de dados em um determinado estado anteriormente à execução de um caso de teste, e também posteriormente. Muito simples de ser utilizado, e além disso possui integração com o Spring (criada pela comunidade). A utilização do mesmo em aplicações que possuem uma estrutura com padrões Java EE promove às seguintes características:
2. COMO UTILIZAR
A seguir será demonstrado como utilizar o DBUnit integrado com o Spring através de uma aplicação-exemplo. Assume-se que o leitor tenha um conhecimento prévio sobre Maven, Spring, JPA e JUnit. O objetivo do código dessa aplicação é ser didático e simples, visando um melhor entendimento por parte do desenvolvedor. Ele está disponibilizado em https://github.com/dclick/dbunit-spring-sample.
2.1 Modelo-Exemplo
O modelo de dados utilizado é uma versão extremamente simplificada de “Usuário e Publicação”. Abaixo o diagrama de classes:
Uma breve explicação do modelo:
O código Java correspondente:
2.2 Dependências Maven
Somente as dependências necessárias para o funcionamento do DBunit com o Spring são exibidas abaixo. Logo, é necessário a adição das demais (Spring, JPA, Logging, Hibernate, …):
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version> <!– Versão utilizada: 4.12 –>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>${dbunit.version}</version> <!– Versão utilizada: 2.5.1 –>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version> <!– Versão utilizada: 4.2.1.RELEASE –>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.springtestdbunit</groupId>
<artifactId>spring-test-dbunit</artifactId>
<version>${spring-test-dbunit.version}</version> <!– Versão utilizada: 1.2.1 –>
<scope>test</scope>
</dependency>
2.3 Configurações do Spring
Além das configurações base do Spring, é necessário adicionar uma configuração de Datasource. Ela deve conter um bean declarado cujo o id seja dataSource. Na aplicação-exemplo, ela ficou assim:
As propriedades utilizadas:
dataSourceClassName=org.hsqldb.jdbc.JDBCDataSource
dataSource.url=jdbc:hsqldb:mem://test
dataSource.user=sa
dataSource.password=
* O datasource configurado está apontando para um banco de dados em memória (HSQLDB). Porém, poderia ser qualquer um suportado pela implementação de DataSource que você utiliza.
2.4 Datasets
Os arquivos que representam os cenários do DBUnit são arquivos .xml comuns que não necessitam de esquemas. Eles são denominados datasets. Na aplicação exemplo, seguindo a estrutura Maven para o código de testes, foi criado um diretório chamado datasets em src/test/resources.
Para que se possa limpar o banco de dados completamente antes (ou depois) da execução de um teste, é necessário criar um dataset que representa o estado vazio do banco. No exemplo, foi criado um arquivo de dataset chamado cenario_limpo.xml. Para representar uma tabela vazia, basta adicionar uma tag com o nome da tabela como demonstrado abaixo:
É muito importante que a ordem das tabelas esteja correta. Essa ordem é definida pela dependência de constraints entre elas. No exemplo, a tabela de publicações tem uma chave estrangeira para a de usuários. Logo a tag <usuario /> foi colocada antes de <publicacao />.
2.4.2 Dataset Com Dados
Agora será demonstrado como criar um cenário de dados. Basicamente, para cada registro (tupla) da tabela, deverá ser adicionada uma tag como o nome referente à tabela a qual pertence. Além disso, é necessário informar o nome das colunas com seus respectivos valores no formato coluna=”valor”.
O cenário criado acima contém um usuário com duas publicações. Para atributos do tipo Date, não é necessário especificar o horário como no exemplo acima.
2.5 Utilizando o DBUnit no seu teste Spring
A integração criada pela comunidade fornece algumas anotações que facilitam demais a utilização do DBUnit. As mais utilizadas são:
* Somente utilize a terceira anotação caso você tenha o controle sobre os ids gerados pela implementação do JPA.
Para o correto funcionamento das anotações descritas acima, a sua classe de testes deve estar anotada com @TestExecutionListeners provida pelo Spring. Além dos Listeners padrões adicionados como argumento, também é necessário adicionar a classe com.github.springtestdbunit.DbUnitTestExecutionListener (é recomendada também a classe org.springframework.test.context.support.DirtiesContextTestExecutionListener).
2.6 Exemplo de Caso de Teste
Finalmente, abaixo está o código de um caso de teste básico que exemplifica a utilização de tudo o que foi explicado anteriormente:
2.6 Campos com Valores Null
Por padrão, o DBUnit não tem um literal ou símbolo específico para colunas com valores null. Entretanto, a integração criada pela comunidade fornece uma maneira de estender o seu comportamento para interpretação dos datasets. Para isso, basta se criar uma classe filha de com.github.springtestdbunit.dataset.AbstractDataSetLoader e redefinir o método createDataSet(). Este método recebe uma instância de org.springframework.core.io.Resource como argumento, representando o arquivo que está sendo interpretado. Para que ela entre ação, é necessário anotar a classe de testes com com.github.springtestdbunit.annotation.DbUnitConfiguration(dataSetLoader=MinhaExtensao.class). Abaixo, uma possível solução para o problema dos valores null:
Com a implementação e configuração acima, pode-se definir um valor null para uma coluna de um registro do dataset colocando-se a String “[null]” . No modelo da aplicação-exemplo, o campo “apelido” de Usuario aceita valores null. Abaixo, uma adição de usuário que não tem apelido no cenário do dataset .
Um caso de teste para verificar se a customização funcionou:
3. CONCLUSÃO
Dentre as soluções possíveis envolvendo problemas com cenários de dados em testes automatizado, o DBUnit se destaca pela simplicidade e elegância. Sua adoção apresenta ganhos para o desenvolvedor e para a aplicação em alguns aspectos-chave. Além da ferramenta solucionar o problema introduzido no começo desse artigo, sua utilização com o framework Spring se mostrou bastante simples e uma alternativa para desenvolvedores Java que o utilizam.