Técnico

DBUnit e Spring

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:

  • Validação do mapeamento objeto relacional;
  • Visualização de problemas envolvendo o modelo de dados (tabelas não normalizadas, ausência de constraints necessárias, ..);
  • Controle sobre o estado da base de dados;
  • Reutilização de cenários de dados;
  • Separação entre dados e lógica nas implementações dos casos de teste

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:

classes

Uma breve explicação do modelo:

  • Usuario: entidade que representa usuários de um sistema de microblog que podem realizar publicações. Um usuário pode ter nenhuma ou muitas publicações associadas.
    1.  e-mail é um campo obrigatório (not null), enquanto apelido é opcional.
    2. a operação publicar() basicamente cria uma nova Publicacao e a associa ao Usuario.
  • Publicacao: entidade que representa publicações de um usuário. Sem um usuário não é possível criar uma nova publicação.
    1. o atributo conteudo, que é obrigatório, seria o texto publicado pelo usuário.
    2. data é um timestamp que contém o dia e horário em que a publicação ocorreu.

O código Java correspondente:

Entidade Usuário

Entidade Usuário

dbunit_spring_publicacao

Entidade Publicação

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:

Configuração de DataSource

Configuração de DataSource

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.

dbunit_spring_pasta2.4.1 Dataset Vazio

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:

cenario_vazio.xml

cenario_limpo.xml

É 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”.

cenario_01.xml

cenario_01.xml

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:

  • @DatabaseSetup: essa anotação informa ao DBUnit qual cenário de dados ele deve utilizar para o seu teste. Ele tentará replicar o estado do banco definido no dataset informado como parâmetro antes da execução do caso de teste. Ex: @DatabaseSetup(“classpath:datasets/cenario_limpo.xml”). Essa anotação pode ser utilizada tanto em um método específico, como na classe de testes. Caso a classe seja anotada, todos os métodos de teste utilizarão o mesmo cenário de dados.
  • @DatabaseTeardown: funciona exatamente como a anterior, porém é acionada após a finalização do(s) caso(s) de teste(s).
  • @ExpectedDatabase: essa anotação recebe como parâmetro a localização do dataset, e o utilizará para comparar com o estado final do banco de dados após a execução do teste. Caso o estado final seja diferente do descrito no dataset, o seu teste falhará. Segue as mesmas regras dos anteriores com relação ao local da anotaçã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:

Caso de Teste

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:

dbunit_spring_datasetloader

dbunit_spring_config_custom

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 .

dbunit_spring_usuario_null

Um caso de teste para verificar se a customização funcionou:

dbunit_spring_teste_usu_null

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.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Compartilhe isso: