Java e OO

Testes de integração com DBUnit

DBUnit é uma extensão do JUnit para facilitar os testes de integração que envolvem banco de dados. Ele irá colocar os dados em um estado conhecido antes da execução de cada teste, assim evitamos problemas que podem ocorrer caso algum cenário corrompa ou altere os dados nos casos seguintes.

A ideia é muito simples, ao invés de você escrever vários SQL com insert e delete, o que seria muito chato e, complicado de dar manutenção, iremos colocar os dados em arquivos XML. O DBUnit irá gerenciar esses arquivos, fazendo todas as operações necessárias.

Chega de conversa e vamos criar um exemplo. Para isso crie um projeto Maven com o pom.xml abaixo:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.algaworks.blog</groupId>
  <artifactId>artigo-teste-integracao-dbunit</artifactId>
  <version>1.0.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.0</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <!-- jUnit 4 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <!-- Hamcrest -->
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-all</artifactId>
      <version>1.3</version>
      <scope>test</scope>
    </dependency>

    <!-- Driver JDBC do MySQL -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.28</version>
      <scope>test</scope>
    </dependency>

    <!-- DBUnit -->
    <dependency>
      <groupId>org.dbunit</groupId>
      <artifactId>dbunit</artifactId>
      <version>2.4.9</version>
      <scope>test</scope>
    </dependency>

    <!-- Necessário para o DBUnit -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.5.6</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.5.6</version>
      <scope>test</scope>
    </dependency>

    <!-- Núcleo do Hibernate -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.3.1.Final</version>
      <scope>compile</scope>
    </dependency>

    <!-- Implementação de EntityManager da JPA -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>4.3.1.Final</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>

Neste exemplo vamos criar dois casos de teste. O primeiro irá retornar carros zero km e outro com o período informado. Veja abaixo os casos de teste:

@Test
public void deveRetornarCarrosZeroKm() {
  List<Carro> resultado = carroDAO.buscarCarrosZero();
    
  assertThat(resultado, hasItems(new Carro(1L), new Carro(4L)));
}

@Test
public void deveRetornarCarrosMenosDoisAnosUso() {
  Integer doisAnosAntes = Calendar.getInstance().get(Calendar.YEAR) - 2;
  List<Carro> resultado = carroDAO.buscarCarrosComIdadeInferiorA(doisAnosAntes);
    
  assertThat(resultado, hasItems(new Carro(1L), new Carro(2L), new Carro(3L), new Carro(4L)));
}

Vamos ver agora o que precisamos fazer para o DBUnit funcionar no nosso caso de teste. Primeiro vamos criar uma classe utilitária para iniciar a conexão com o banco de dados e um método que irá executar as operações SQL, como o INSERT e DELETE.

package com.algaworks.blog.dbunit.dao.helper;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.ext.mysql.MySqlDataTypeFactory;
import org.dbunit.operation.DatabaseOperation;

public class DbUnitHelper {

  private Connection conexao;
  private DatabaseConnection conexaoDBUnit;
  private String xmlFolder;

  public DbUnitHelper(String xmlFolder) {
    this.xmlFolder = xmlFolder;

    try {
      Class.forName("com.mysql.jdbc.Driver").newInstance();
      conexao = DriverManager.getConnection("jdbc:mysql://localhost/artigo_teste_integracao_dbunit", "root", "root");
      conexaoDBUnit = new DatabaseConnection(conexao);
      DatabaseConfig config = conexaoDBUnit.getConfig();
      config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
    } catch (Exception e) {
      throw new RuntimeException("Erro inicializando DBUnit", e);
    }
  }

  public void execute(DatabaseOperation operation, String xml) {
    try {
      InputStream is = getClass().getResourceAsStream("/" + xmlFolder + "/" + xml);
      FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
      IDataSet dataSet = builder.build(is);

      operation.execute(conexaoDBUnit, dataSet);
    } catch (Exception e) {
      throw new RuntimeException("Erro executando DbUnit", e);
    }
  }

  public void close() {
    try {
      conexaoDBUnit.close();
      conexao.close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }
}

Reparou que o método execute recebe uma String? Esse é o nome do arquivo xml que contém os dados do banco de dados. Coloque o arquivo em src/test/resources/DbUnitXml com o conteúdo abaixo:

<?xml version="1.0" encoding="UTF-8"?>

<dataset>
  <carro codigo="1" placa="AAA-1111" anoFabricacao="2014" />
  <carro codigo="2" placa="AAA-1112" anoFabricacao="2013" />
  <carro codigo="3" placa="AAA-1113" anoFabricacao="2012" />
  <carro codigo="4" placa="AAA-1114" anoFabricacao="2014" />
  <carro codigo="5" placa="AAA-1115" anoFabricacao="2011" />
  <carro codigo="6" placa="AAA-1116" anoFabricacao="2010" />
  <carro codigo="7" placa="AAA-1117" anoFabricacao="2010" />
</dataset>

Agora já podemos inicializar na classe de teste. Veja a classe completa abaixo:

package com.algaworks.blog.dbunit.dao;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

import java.util.Calendar;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.dbunit.operation.DatabaseOperation;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.algaworks.blog.dbunit.dao.helper.DbUnitHelper;
import com.algaworks.blog.dbunit.model.Carro;

public class CarroDAOTest {

  private static DbUnitHelper dbUnitHelper;
  private static EntityManagerFactory factory;
  
  private EntityManager manager;
  private CarroDAO carroDAO;

  @BeforeClass
  public static void initClass() {
    dbUnitHelper = new DbUnitHelper("DbUnitXml");
    factory = Persistence.createEntityManagerFactory("artigoTesteIntegracaoDbunitPU");
  }

  @Before
  public void init() {
    dbUnitHelper.execute(DatabaseOperation.DELETE_ALL, "Carro.xml");

    dbUnitHelper.execute(DatabaseOperation.INSERT, "Carro.xml");

    manager = factory.createEntityManager();
    this.carroDAO = new CarroDAO(manager);
  }
  
  @After
  public void end() {
    this.manager.close();
  }

  @Test
  public void deveRetornarCarrosZeroKm() {
    List<Carro> resultado = carroDAO.buscarCarrosZero();
    
    assertThat(resultado, hasItems(new Carro(1L), new Carro(4L)));
  }

  @Test
  public void deveRetornarCarrosMenosDoisAnosUso() {
    Integer doisAnosAntes = Calendar.getInstance().get(Calendar.YEAR) - 2;
    List<Carro> resultado = carroDAO.buscarCarrosComIdadeInferiorA(doisAnosAntes);
    
    assertThat(resultado, hasItems(new Carro(1L), new Carro(2L), new Carro(3L), new Carro(4L)));
  }

}

Veja que antes da execução de cada teste, todos os carros são removidos e depois inseridos novamente, de uma forma muito simples, com apenas um arquivo Carro.xml. Nesse mesmo lugar inicializamos o DAO com o EntityManager para testarmos as consultas.

Caso algum caso de teste altere o banco de dados, inserindo ou removendo dados, não irá atrapalhar em nada os outros, pois tudo sempre é refeito.

Não sei se você percebeu, mas dependendo do cenário essa exclusão e inserção pode ser muito demorada e você pode usar uma outra estratégia. Você pode simplesmente não apagar os dados entre os casos de teste e assim ganhar esse tempo mas, faça com bastante cuidado para não adicionar algum teste que possa corromper os dados.

Para aprender mais sobre JPA, Hibernate, DBUnit e jIntegrity, além de diversas outras tecnologias, conheça os cursos online da AlgaWorks, que são completos e substituem a necessidade de cursos presenciais.

Acesse ou baixe o código-fonte completo deste artigo no GitHub.

Graduado em Engenharia Elétrica pela Universidade Federal de Uberlândia e detentor das certificações LPIC-1, SCJP e SCWCD.

Olá,

o que você achou deste conteúdo? Conte nos comentários.

Junte-se a mais de 100.000 pessoas

Entre para nossa lista e receba conteúdos exclusivos e com prioridade

Você se Inscreveu com Sucesso!