Testando aplicações JEE6 com Arquillian

Criar testes automatizados para aplicações JEE sempre foi um desafio, pois exige que um container esteja funcionando para prover os serviços necessários que a aplicação precisa para funcionar e consequentemente para que os testes possam ser executados.

O framework Arquillian vem para facilitar -e muito- essa tarefa. Seu objetivo é prover uma plataforma de testes poderosa que cuida de vários aspectos ao testar uma aplicação JEE, como fazer o deploy da aplicação no container, subir o container, executar os testes e derrubar o container. Ele também permite que você enriqueça suas classes de testes usando anotações como @Inject, @EJB e @Resource. Dessa forma você pode criar testes funcionais bem mais reais e eficazes.

Ele também permite que o usuário escolha tanto o JUnit como com o TestNG para criação e execução dos testes, além de permitir a execução em vários containers, como JBoss, Glasshfish e Tomcat.

O objetivo desse post não é mostrar os benefícios de escrever testes automatizados -usando ou não TDD-, e sim focar no uso do Arquillian através de um exemplo prático. Apenas um comentário sobre escrever testes para seu código: Não acho que seja necessário escrever testes para 100% do código que você produz, mas entregar código sem teste pelo menos para as partes mais importantes, é entregar código pela metade.

Aplicação usando Arquillian

Para mostrar o uso do Arquillian, vamos criar um simples módulo/componente JEE6 cujo objetivo é permitir o cadastro e busca de clientes. Para empacotamento e gerenciamento de builds, será utilizado o maven. O container utilizado será o JBoss 7.1.0-final e para executar os testes será usado o JUnit 4.8.2. Como banco de dados, será usado o próprio HQSLDB,
banco de dados embarcado do JBoss e o datasource ExampleDS que já vem configurado.
No final do post é disponibilizado link para download do projeto.

pom.xml

<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.wordpress.lucianomolinari</groupId>
	<artifactId>exemplo_arquillian</artifactId>
	<version>1.0.0</version>

	<properties>
		<version.arquillian_core>1.0.0.CR7</version.arquillian_core>
		<version.arquillian_container>7.1.0.Final</version.arquillian_container>
	</properties>

	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
		</repository>
	</repositories>

	<!-- Define o profile para execucao dos testes unitarios com Arquillian -->
	<profiles>
		<profile>
			<id>jbossas-7</id>
			<dependencies>
				<dependency>
					<groupId>org.jboss.arquillian.junit</groupId>
					<artifactId>arquillian-junit-container</artifactId>
					<version>${version.arquillian_core}</version>
					<scope>test</scope>
				</dependency>
				<dependency>
					<groupId>org.jboss.as</groupId>
					<artifactId>jboss-as-arquillian-container-managed</artifactId>
					<version>${version.arquillian_container}</version>
					<scope>test</scope>
				</dependency>
			</dependencies>
		</profile>
	</profiles>

	<dependencies>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-api</artifactId>
			<version>6.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

</project>

O pom do projeto é bem simples. O destaque fica por conta do profile jbossas-7, que define as dependências para execução dos testes com o arquillian.
A primeira dependência importa a biblioteca core do arquillian, que contém as classes e anotações necessárias para criação das classes de testes. A segunda importa o módulo para execução dos testes no jboss-7.1.0.Final, que será executado no modo managed. O legal é que o seu código de teste não referencia nada dessa lib, somente da core. Isso significa que o mesmo código de teste pode ser usado em vários containers sem mudança no seu código. Na página do arquillian pode-se encontrar quais containers são suportados pelo framework e qual a diferença entre os modos de execução remote, managed e embedded.
No nosso exemplo estamos usando o modo managed, o que significa que o próprio arquillian vai subir e descer o servidor, além de fazer o deploy/undeploy da aplicação. É necessário que você tenha o JBoss instalado na sua máquina e que a variável JBOSS_HOME esteja corretamente configurada.

Código do módulo/componente

O componente a ser construído é bem simples e é constituido por:

  • Uma entidade Cliente para o mapeamento ORM
  • Uma interface Local com os métodos de negócio
  • Um Stateless Session Bean implementado a interface de negócio
  • Um Stateless Session Bean fazendo papel de DAO

O código Java criado no projeto é extremamente simples e dispensa maiores comentários.

Entidade Cliente – Cliente.java
package com.wordpress.lucianomolinari.exemplo_arquillian.cliente.entity;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "cliente")
public class Cliente implements Serializable {
	private static final long serialVersionUID = 7796759140893817571L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	private String nome;

	//construtores, getters/setters, equals, hashCode e toString omitidos

}
Interface de negócio – ClienteServices.java
package com.wordpress.lucianomolinari.exemplo_arquillian.cliente.services;

import java.util.List;

import javax.ejb.Local;

import com.wordpress.lucianomolinari.exemplo_arquillian.cliente.entity.Cliente;

@Local
public interface ClienteServices {

	Cliente add(Cliente cliente);

	List<Cliente> findAll();

}
Dao – ClienteDao.java
package com.wordpress.lucianomolinari.exemplo_arquillian.cliente.dao;

import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.wordpress.lucianomolinari.exemplo_arquillian.cliente.entity.Cliente;

@Stateless
public class ClienteDao {

	@PersistenceContext
	private EntityManager em;

	public Cliente add(Cliente cliente) {
		em.persist(cliente);
		return cliente;
	}

	@SuppressWarnings("unchecked")
	public List<Cliente> findAll() {
		return em.createQuery("from Cliente c order by c.id").getResultList();
	}

}
Implementação da interface de negócio – ClienteServicesImpl.java
package com.wordpress.lucianomolinari.exemplo_arquillian.cliente.services.impl;

import java.util.List;

import javax.ejb.Stateless;
import javax.inject.Inject;

import com.wordpress.lucianomolinari.exemplo_arquillian.cliente.dao.ClienteDao;
import com.wordpress.lucianomolinari.exemplo_arquillian.cliente.entity.Cliente;
import com.wordpress.lucianomolinari.exemplo_arquillian.cliente.services.ClienteServices;

@Stateless
public class ClienteServicesImpl implements ClienteServices {

	@Inject
	private ClienteDao clienteDao;

	public Cliente add(Cliente cliente) {
		return clienteDao.add(cliente);
	}

	public List<Cliente> findAll() {
		return clienteDao.findAll();
	}

}

E deve ser criado o arquivo de marcação beans.xml dentro de src/main/resources/META-INF.

Códigos/Recursos de teste

É composto por uma classe com o teste propriamente dita e pelo arquivo test-persistence.xml dentro de src/test/resources/.

test-persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
	xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
	<persistence-unit name="livrariaxPU" transaction-type="JTA">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
		<properties>
			<property name="hibernate.hbm2ddl.auto" value="create-drop" />
			<property name="hibernate.show_sql" value="true" />
		</properties>
	</persistence-unit>
</persistence>
TestClienteServices.java
package com.wordpress.lucianomolinari.exemplo_arquillian.cliente.services;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.util.List;

import javax.inject.Inject;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

import com.wordpress.lucianomolinari.exemplo_arquillian.cliente.entity.Cliente;

@RunWith(Arquillian.class)
public class TestClienteServices {

	@Inject
	private ClienteServices clienteServices;

	@Deployment
	public static JavaArchive createTestArchive() {
		return ShrinkWrap.create(JavaArchive.class, "testCliente.jar")
				.addPackages(true, "com.wordpress.lucianomolinari.exemplo_arquillian.cliente")
				.addAsManifestResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"))
				.addAsManifestResource("test-persistence.xml", "persistence.xml");
	}

	@Test
	public void testAdicionaEConsultaClientes() {
		List<Cliente> clientes = clienteServices.findAll();

		assertNotNull(clientes);
		assertEquals(0, clientes.size());

		clienteServices.add(new Cliente("Cliente 1"));
		clienteServices.add(new Cliente("Cliente 2"));

		clientes = clienteServices.findAll();
		assertNotNull(clientes);
		assertEquals(2, clientes.size());
		assertEquals("Cliente 1", clientes.get(0).getNome());
		assertEquals("Cliente 2", clientes.get(1).getNome());
	}

}

Essa é classe mais importante do projeto, então vamos por partes:

A anotação @RunWith(Arquillian.class) indica que esse codigo JUnit deve ser executado pelo Arquillian. Sem essa anotação, a execução dos testes nao funcionaria. Repare na anotação @Inject, é como se estivesse em um código de produção, sem nenhuma diferença. Pode-se injetar qualquer tipo de classe CDI, EJB e Resource.

Para executar os testes o arquillian pega o codigo de teste, empacota e coloca pra rodar direto no servidor. Para isso, deve-se definir o tipo de empacotamento para os testes e quais arquivos serão empacotados. Isso é feito através de um método public e static com a anotação @Deployment e o tipo de retorno deve ser a forma como as classes ncessárias para a execução do teste serão empacotadas. Existem quatro formas: JavaArchive, EnterpriseArchive, ResourceAdapterArchive e WebArchive. No nosso exemplo, será utilizado JavaArchive.
Nesse método são feitas várias tarefas para o empacotamento:

  • O Arquillian usa a biblioteca ShrinkWrap para criar o pacote java em runtime
  • Na primeira linha é informado o tipo de empacotamento e o nome do pacote
  • Na segunda linha é definido quais classes serão empacotadas. No exemplo, é importado o pacote com.wordpress.lucianomolinari.exemplo_arquillian.cliente e todos abaixo dele, como cliente.entity, cliente.services e cliente.dao. Isso só é possível devido ao valor true no primeiro parâmetro, que diz que a importação deve ser recursiva. Também pode-se usar os métodos addClass e addClasses e todos esses podem ser usados em conjunto.
  • Na terceira linha é informado para criar um arquivo vazio chamando beans.xml e adicioná-lo como um manifest resource. O CDI precisa desse arquivo para funcionar.
  • Na última linha o arquivo test-persistence.xml é adicionado como um manifest resource com o nome persistence.xml. Isso é muito útil, pois pode-se ter um persistence.xml dentro src/main/resources/META-INF com os dados do banco real e outro (no caso src/test/resources/test-persistence.xml) para ser usado durante a execução dos testes. Lembrando que esse pacote gerado pelo arquillian não tem nada a ver com o artefato real, esse é um .jar a parte em que só são empacotados os arquivos/códigos informados por nós e a classe de teste. Por isso é possível copiar o test-persistence.xml como persistence.xml.

Por fim, para criação dos testes, basta usar JUnit da forma usual. Veja que não existe nenhum mock, é um teste de integração real, usando recursos reais.

Eu geralmente não uso o HSQLDB do JBoss para os testes. Eu prefiro ter 2 bases no banco real, uma para produção/testes manuais e outra para os testes automatizados. Porém isso pode gerar um problema, já que precisaríamos declarar os 2 datasources no JBoss. Como comentei em meu outro post sobre datasources no JBoss 7, é possível criar datasources de dentro da aplicação. Então, para resolver essa questão eu sigo a seguinte abordagem:

  • Crio um arquivo test-ds.xml (pode ser outro nome) dentro de src/test/resources e declaro um datasource apontando para o banco de dados real, porém para uma base de testes
  • Altero o arquivo src/test/resources/test-persistence.xml para apontar para o datasource de testes
  • Adiciono o arquivo test-ds.xml como um manifest resource na criação do arquivo de testes, adicionando a linha addAsManifestResource(“test-ds.xml”)

Dessa forma, o Arquillian vai empacotar o datasource de testes, o JBoss vai reconhecer o arquivo como um datasource (tem o sufixo *-ds.xml) e fazer seu deploy. Com isso, além do seu teste já ser bem funcional e não usar mocks, você também usa o BD real.

IDE

Para configurar o projeto e executar os testes em sua IDE preferida, basta dar uma conferida na seção “Getting Started” do Reference Guide.

Executando os testes via linha de comando

Para executar os testes pelo Maven via linha de comando, o seguinte comando deve ser utilizado:
mvn test -Pjbossas-7

Dessa forma os testes serão executados usando o profile jbossas-7, previamente configurado no pom.xml

Conclusões

A criação e execução de testes automatizados é uma tarefa imprescindível no desenvolvimento de software. Quanto mais real os testes, mais eficazes eles serão. O arquillian endereça justamente esse ponto de tornar seus testes mais reais e consequentemente mais eficazes, além de facilitar a criação de testes para aplicações JEE6.

O código fonte do projeto pode ser baixado aqui. Qualquer dúvida, fique a vontade em usar o espaço de comentários aqui do blog ou via twitter. Também não deixe de conferir a documentação do arquillian.


Quer aprender muito mais? Não deixe de ver meu curso Construa uma aplicação do zero com JEE 7, Java 8 e Wildfly.

Esta entrada foi publicada em Java EE, JBoss com as etiquetas , , , , . ligação permanente.

14 respostas a Testando aplicações JEE6 com Arquillian

  1. Legal cara… Gostei muito do post
    Parabéns
    Abraços

  2. Fabrício Massula diz:

    Ótimo!

  3. Parabéns, excelente artigo: Claro, objetivo e original.

  4. Vercetti diz:

    Muito bom! Não conhecia nada sobre testes com Arquillian, esse tutorial me ajudou muito, já que o da página do projeto não funcionou aqui hehehe. Abraços.

  5. Balde diz:

    Post extremamente útil e muito bem explicado!
    Parabéns!

  6. Thiago Vargas diz:

    Cara, eu fiz parecido, porém EJB não funciona, mas CDI funciona (@Inject funciona normalmente, porém se a classe tiver @Stateless ou algo do genero, a injeção não funciona mais, seja com Inject ou @EJB). As funções RestEasy também não funciona quando em teste JUnit, tens alguma ideia?

  7. André Trindade diz:

    Olá.
    Geralmente quem trabalha com Java escreve de maneira pouco clara, sem contextualização e pensando muito em “programar”. Você tem o talento de escrever de maneira clara, ficou excelente o tutorial, meus parabéns!

  8. Pingback: Arquillian Guides - <Armando-Couto/>

Deixe um comentário