Testes de Integração: Validando ainda mais sua aplicação

A próxima etapa nos artigos sobre teste. Vamos aos chamados Testes de Integração. Como tradição, vamos a “definição wiki de ser”. “teste de integração é a fase do teste de software em que módulos são combinados e testados em grupo.”.

Bem, não é a definição mais clara que podemos encontrar. Mas calma, vejamos desse jeito:

Nos testes unitários, cada módulo do sistema será testado individualmente. Sem influência de outras classes do sistema e se possuir alguma dependência, nós criamos Mocks que simulem o funcionamento dessa dependência.

Nos testes de integração, nós queremos que os módulos sejam testados de forma que interajam com o restante do sistema, oferecendo dependências reais para garantir que elas funcionem em conjunto.

Imagine a situação de um cadastro (aquele crud “basicão”). Com o Teste de Unidade, você cria um teste para as regras de negócios, outro para as classes de persistência com o banco e assim por diante. Nos Testes de Integração, você testa somente o método Inserir, ou Pesquisar ou Atualizar e deixa que os módulos si relacionem de verdade, sem Mock Objects, sem nenhum artifício qualquer.

Para realizar nossos testes, novamente utilizaremos o JUnit,  Spring Framework, e o DBUnit, que é uma extensão do JUnit que oferece funcionalidades específicas para testes envolvendo banco de dados.

Primeiramente você pode encontrar uma melhor explicação sobre o DBUnit aqui. O DBUnit necessita de um arquivo xml que funcionará como o conjunto dos dados que serão inseridos no banco antes dos testes rodarem, afim de garantir que os dados que serão usados estarão presentes no banco. Tomamos o exemplo da tabela Filme, com os campos, codigo, titulo e ano.

<dataset>

 <filme codigo="10" titulo="HANNIBAL" ano="2002"/>
 <filme codigo="20" titulo="RED DRAGON" ano="2003"/>
 <filme codigo="30" titulo="ADVOGADO DO DIABO" ano="1997"/>
 <filme codigo="35" titulo="PERFUME DE MULHER" ano="1991"/>
 <filme codigo="40" titulo="THE GODFATHER" ano="1970"/>

</dataset>]
Mas antes de implementar-mos nossos testes, precisamos configurar o dataSource e adicionar o dbUnit no Spring.
<context:component-scan base-package="com.wordpress.yuriadamsmaia.integration.base.dbunit"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource" >

<property name="driverClassName" value="com.mysql.jdbc.Driver" />
 <property name="url" value="suaURL" />
 <property name="username" value="SEUUSERNAME" />
 <property name="password" value="SUAPASSWORD" />
 <property name="suppressClose" value="true" />
 </bean>
Antes de escrever os testes, vamos conhecer o que iremos testar. Para não estender muito, iremos limitar a testar um único método(no caso o método pesquisar).  *
@Service("filmeService")
public class FilmeServiceImpl implements FilmeService{
 @PersistenceContext
 private EntityManager entityManager;
 private Session getSession() {
 return ((Session) entityManager.getDelegate());
 }
@SuppressWarnings("unchecked")
@Override
public List<Filme> pesquisar(Filme filme) {
Criteria criteria = getSession().createCriteria(Filme.class);

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

if (!StringUtils.isEmpty(filme.getTitulo()))
criteria.add(Restrictions.like("titulo", "%" + filme.getTitulo() + "%"));
if (!StringUtils.isEmpty(filme.getAno()))</p>
criteria.add(Restrictions.like("ano", "%" + filme.getAno() + "%"));

criteria.addOrder(Order.asc("codigo"));
return criteria.list();
}

}
* Estamos utilizando o hibernate e JPA API para  a persistência. Então toda configuração no Spring para permitir essa integração será disponivel no final do post.

Escrevendo nosso teste:

O teste terá os seguintes passos:
  1. Limpar e Carregar os dados antes para nossos métodos de teste;
  2. Realizar os métodos de teste, onde nós iremos comparar se o valor está conforme o arquivo XML.
A classe FilmeServiceImplTeste.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
 "file:./WebRoot/WEB-INF/config/spring/applicationContext.xml",
 "file:./WebRoot/WEB-INF/config/spring/applicationContext-persistence-test.xml"
})
public class FilmeServiceImplTeste {
private static final String DATASET = "./test/com/wordpress/yuriadamsmaia/integration/base/dbunit/xml/FilmeServiceImplTeste.xml";
@Autowired DbUnitManager dbUnitManager;
@Autowired FilmeService filmeService;

 @Before
 public void setUp() {
 dbUnitManager.cleanAndInsert(DATASET);
 }
Anotamos a classe com @RunWith(SpringJUnit4ClassRunner.class) para o JUnit rodar os testes com o Spring Test. E também anotamos a classe com o @ContextConfiguration(locations={“file:./WebRoot/WEB-INF/config/spring/applicationContext.xml”, “file:./WebRoot/WEB-INF/config/spring/applicationContext-persistence-test.xml”}), que define os nossos arquivos XML onde estão as configurações, com o detalhe que criamos um outro XML(applicationContext-persistence-test.xml) somente para os testes.
O método cleanAndInsert(DATASET) da Interface DbUnitManager que está sendo injetada, faz com que sempre quando o teste é iniciado,  base de dados é limpa e os dados são re-inseridos(evitando duplicata de dados, ou seja, lixo).
public void cleanAndInsert(String dbUnitXmlPath) {

try {
 IDatabaseConnection dbconn = this.getDbUnitConnection();
 DatabaseOperation.CLEAN_INSERT.execute(dbconn, this.getDataSetFrom(dbUnitXmlPath));
 dbconn.close();
 } catch (Exception e) {
 e.printStackTrace();
 throw new RuntimeException(e);
 }
 }
private IDatabaseConnection getDbUnitConnection() throws      DatabaseUnitException, SQLException {
IDatabaseConnection dbconn = new DatabaseConnection(this.getConnection());
dbconn.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYP E_FACTORY, new MySqlDataTypeFactory());
return dbconn;

}
 private IDataSet getDataSetFrom(String dbUnitXmlPath) throws IOException,
 DataSetException, FileNotFoundException {
 return new FlatXmlDataSetBuilder().build(new FileInputStream(dbUnitXmlPath));
 }
Agora os testes. Daqui pra frente , o raciocínio não muda dos testes unitários.
@Test
 public void deveriaPesquisarFilmesSemCamposEspecificados(){
 Filme filme = new Filme();
 List<Filme> pesquisa = filmeService.pesquisar(filme);
 verificaListaRetornoPesquisa(pesquisa);
 assertEquals("deveria estar trazendo a lista de tamanho correto", pesquisa.size(), LISTA_TAMANHO);
 }
private void verificaListaRetornoPesquisa(List<Filme> pesquisa) {</p>
assertNotNull("nao deveria ser nula", pesquisa);</p>
assertFalse("nao deveria retornar a lista vazia", pesquisa.isEmpty());</p>
assertEquals("deveria estar trazendo a lista correta", pesquisa.get(0).getTitulo(), FILME_TITULO);</p>
assertEquals("deveria estar trazendo a lista correta", pesquisa.get(0).getAno(), FILME_ANO);</p>

}

De fato, como estou passando como argumento do pesquisar um objeto filme com todos os campos vazios, o método retorna todos os registros na tabela. Fazemos as verificações, se a lista retornada não é nula, não é vazia, verificamos se o primeiro elemento da lista retornada está de acordo com o arquivo xml. E finalmente verificamos o tamanho da lista, a fim de saber se todos os elementos foram pesquisados.

De forma análoga, podemos realizar uma pesquisa limitada por algum campo, por exemplo o título.

@Test
public void deveriaPesquisarFilmesPeloTitulo(){
 Filme filme = new Filme();
 filme.setTitulo(FILME_TITULO);
 List<Filme> pesquisa = filmeService.pesquisar(filme);
 verificaListaRetornoPesquisaPeloTitulo(pesquisa);
}
private void verificaListaRetornoPesquisaPeloTitulo(List<Filme> pesquisa) {
 assertNotNull("nao deveria ser nula", pesquisa);
 assertFalse("nao deveria retornar a lista vazia", pesquisa.isEmpty());
 assertEquals("deveria estar trazendo a lista correta", pesquisa.get(0).getTitulo(), FILME_TITULO);
}
Espero ter passado o conhecimento que tenho adquirido nas ultimas semanas sobre Testes. O projeto vai estar disponível no github para download. E o próximo passo será automatizá-los, via Apache Ant.
link para download: https://github.com/yuriadams/VRaptorTestesAutomatizados

4 Responses to Testes de Integração: Validando ainda mais sua aplicação

  1. Diogo Capistrano disse:

    Muito bom o artigo Yuri.

    Quando fui tentar fazer esse teste na minha aplicação aconteceu esse erro:

    Caused by: java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/persistence/PersistenceException
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:195)

    Você passou por isso também?

    Eu inclui a lib persistence-api no meu projeto e nada, teria alguma sugestão?

  2. wbotelhos disse:

    Estou para criar um artigo sobre Teste de Integração também.
    Depois dá uma olhada no jIntegrity que irá facilitar sua vida: http://twitter.com/jintegrity

    Abraço.

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: