Arquillian [Parte 2]

Como visto na primeira publicação sobre Arquillian, é possível testar aplicações com recursos do JavaEE, como EJB e CDI, já no servidor real, sem precisar usar mocks. Nessa publicação será demonstrado como construir testes de recursos que utilizam JPA (Java Persistence API) utilizando Arquillian.

O Arquillian possui suporte a vários contêiners JavaEE. Nos exemplos a seguir, será utilizado o servidor GlassFish Embarcado que evitará qualquer necessidade de instalações e proverá todos os recursos padrões. 

Será demonstrado:

  • Como fazer uma entidade ser persistida usando o recurso JPA EntityManager
  • Como fazer consultas na base de dados usando JPQL ou Criteria API

O código fonte dos exemplos abaixo podem ser acessado neste link.

Para os exemplos a seguir, é necessário ter o Maven disponível, seja no prompt de comando, ou no IDE. Caso não tenha, acesse  Maven. Também é necessário ter JDK  na versão 1.5 ou maior instalado, apesar da versão 1.6 ser a recomendada.

 

O exemplo a seguir terá a seguinte estrutura:

Arquillian2_Arquivos

 Figura 1 – Estrutura do projeto

No projeto, crie uma classe ContaCorrente com mapeamento JPA, da qual contêm os atributos:

  • “id” – a chave primária
  • “numero” – número da conta
  • “digito”  –  dígito da conta
  • “cnpjCpf” – CNPJ ou CPF do titular da conta
  • “agencia” – agência da conta corrente

Crie também a classe Agencia com os seguintes atributos:

  • “id” – a chave primária
  • “numero” – número da agência
  • “nome” – nome da agência
  • “cnpj” – CNPJ da agência
  • “endereco” – endereço da agência

Segue as classes:

package com.matera.arquillian;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
@Table(name = "TB_AGENCIA_BANCARIA")
public class Agencia implements Serializable {

@Id
@GeneratedValue
@Column(name = "ID")
private Long id;

@Size(min = 4, max = 50)
@NotNull
@Column(name = "NOME")
private String nome;

@Min(1)
@Max(9999)
@NotNull
@Column(name = "NUMERO")
private Integer numero;

@Size(min = 7, max = 14)
@NotNull
@Column(name = "CNPJ")
private String cnpj;

@Size(min = 10, max = 100)
@NotNull
@Column(name = "ENDERECO")
private String endereco;

// Getters e Setters
}
Listagem 1 – Entidade
package com.matera.arquillian;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
@Table(name = "TB_CONTA_CORRENTE")
public class ContaCorrente implements Serializable {

@Id
@GeneratedValue
@Column(name = "ID")
private Long id;

@Max(99999)
@Min(1)
@NotNull
@Column(name = "NUMERO_CONTA")
private Integer numero;

@Max(9)
@Min(0)
@NotNull
@Column(name = "DIGITO_CONTA")
private Integer digito;

@Size(min = 3, max = 14)
@NotNull
@Column(name = "CNPJ_CPF")
private String cnpjCpf;

@OneToOne
@JoinColumn(name = "AGENCIA_ID")
private Agencia agencia;

// Getters e Setters
}
Listagem 2 – Entidade
package com.matera.arquillian;

import java.io.Serializable;
import java.util.List;

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

@Stateless
@Local
public class ContaCorrenteDAO implements Serializable {

@PersistenceContext
private EntityManager em;

public void salvar(ContaCorrente contaCorrente) {

em.persist(contaCorrente);
}

public ContaCorrente alterar(ContaCorrente contaCorrente) {

return em.merge(contaCorrente);
}

public void apagar(ContaCorrente contaCorrente) {

em.remove(contaCorrente);
}

public ContaCorrente buscarPorID(Long id) {

return em.find(ContaCorrente.class, id);
}

public List<ContaCorrente> buscarTodos() {

return em.createQuery("SELECT cc FROM ContaCorrente cc ORDER BY cc.id").getResultList();
}

public List<ContaCorrente> buscarPorCnpjCpf(String cnpjCpf) {

return em.createQuery("SELECT cc FROM ContaCorrente cc WHERE cc.cnpjCpf = :cnpjCpf ORDER BY cc.id")
.setParameter("cnpjCpf", cnpjCpf).getResultList();
}
}
Listagem 3 – DAO

Agora para testar a classe ContaCorrenteDAO, basta criar uma classe de testes do Aquillian com @RunWith(Arquillian.class) e empacotar os recursos em um método estático com @Deployment usando o ShrinkWrap. Para o uso do JPA, os passos abaixo devem ser observados:

  1. @PersistenceContext EntityManager, injeta o contexto de persistencia diretamente no teste, assim como ocorreria em um EJB.
  2. @Inject UserTransaction, injeta uma transação JTA (Java Transaction API) diretamente no teste, usando o recurso de CDI
  3. @EJB ContaCorrenteDAO, injeta o objeto diretamente no teste, usando o recurso EJB
  4. @Before e @After, metodos que serão executados de dentro do servidor através do Arquillian antes e depois de cada teste, respectivamente. Um detalhe importante é que @Before é executado somente depois de todas as injeções já tiverem ocorrido.
  5. Incluir o test-persistence.xml em /src/test/resources-glassfish-embedded/
    <?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="test">
    <jta-data-source>jdbc/arquillian</jta-data-source>
    <properties>
    <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
    <property name="eclipselink.logging.level.sql" value="FINE" />
    <property name="eclipselink.logging.parameters" value="true" />
    </properties>
    </persistence-unit>
    </persistence>
    
    Listagem 4 – test-persistence.xml
  1. Incluir o test-persistence.xml no empacotamento o ShrinkWrap.
    .addAsResource("test-persistence.xml", "META-INF/persistence.xml")
    
  1. Incluir o glassfish-resources.xml em /src/test/resources-glassfish-embedded/ do qual contém a contiguração do DataSource
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
    <resources>
    <jdbc-resource pool-name="ArquillianEmbeddedDerbyPool" jndi-name="jdbc/arquillian" />
    <jdbc-connection-pool name="ArquillianEmbeddedDerbyPool" res-type="javax.sql.DataSource" datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource" is-isolation-level-uaranteed="false">
    <property name="databaseName" value="target/databases/derby" />
    <property name="createDatabase" value="create" />
    </jdbc-connection-pool>
    </resources>
    
    Listagem 5 – glassfish-resources.xml

Vale ressaltar que é possível utilizar um arquivo de persistence.xml diferente do arquivo real, permitindo o uso de algo como um Data Source criado somente para testes sem afetar a configuração real.

E também src/test/resources-glassfish-embedded/logging.properties:

handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%4$s: %5$s%n
java.util.logging.ConsoleHandler.level=FINEST
Listagem 6 – logging.properties

O Persistence Unit configurado em test-persistence.xml se refere a DataSource chamado “jdbc/matera_arquillian”, que será usado para executar os testes com GlassFish. Note que usamos @PersistenceContext sem especificar o nome do DataSource, na classe ContaCorrenteDAO. Caso estivéssemos usando mais de um DataSource, seria necessário especificar nesta anotação.

package com.matera.arquillian;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.ejb.EJB;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.UserTransaction;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class ContaCorrenteDAOTest {

@Deployment
public static Archive<?> createDeployment() {

return ShrinkWrap.create(WebArchive.class, "test.war").addPackage(ContaCorrente.class.getPackage())
.addPackage(ContaCorrenteDAO.class.getPackage())
.addAsResource("test-persistence.xml", "META-INF/persistence.xml")
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}

@EJB
ContaCorrenteDAO dao;

@PersistenceContext
EntityManager em;

@Inject
UserTransaction utx;

private List<ContaCorrente> contas;

@Before
public void preparePersistenceTest() throws Exception {

clearData();
restaurarDados();
insertData();
startTransaction();
}

@Test
public void testSalvar() throws Exception {

int i = 6;
ContaCorrente cc = new ContaCorrente();
cc.setAgencia(dao.buscarPorID(50L).getAgencia());
cc.setCnpjCpf("4443332220" + i);
cc.setDigito(i);
cc.setId(10L * i);
cc.setNumero(1000 * i);
dao.salvar(cc);
List<ContaCorrente> todos = dao.buscarTodos();
assertCCEquals(todos.get(0), contas.get(0));
assertCCEquals(todos.get(1), contas.get(1));
assertCCEquals(todos.get(2), contas.get(2));
assertCCEquals(todos.get(3), contas.get(3));
assertCCEquals(todos.get(4), contas.get(4));
assertCCEquals(todos.get(5), cc);
}

@Test(expected = Exception.class)
public void testSalvarErroConstraint() throws Exception {

ContaCorrente cc = new ContaCorrente();
cc.setAgencia(contas.get(4).getAgencia());
dao.salvar(cc);
}

@Test
public void testAlterar() throws Exception {

int i = 6;
ContaCorrente cc = dao.buscarPorID(50L);
cc.setCnpjCpf("4443332220" + i);
cc.setDigito(i);
cc.setNumero(1000 * i);
dao.salvar(cc);

// Offline
ContaCorrente ccEsperado = new ContaCorrente();
ccEsperado.setAgencia(contas.get(4).getAgencia());
ccEsperado.setCnpjCpf("4443332220" + i);
ccEsperado.setDigito(i);
ccEsperado.setId(50L);
ccEsperado.setNumero(1000 * i);
assertCCEquals(ccEsperado, cc);
}

@Test
public void testRemover() throws Exception {

List<ContaCorrente> todos = dao.buscarTodos();
dao.apagar(todos.get(0));
dao.apagar(todos.get(2));
dao.apagar(todos.get(4));

List<ContaCorrente> restantes = dao.buscarTodos();
Assert.assertEquals(2, restantes.size());
assertCCEquals(contas.get(1), restantes.get(0));
assertCCEquals(contas.get(3), restantes.get(1));
}

@Test
public void testFiltroPorCnpjCpf() throws Exception {

ContaCorrente novo = contas.get(2);
novo.setId(60L);
dao.salvar(novo);
restaurarDados();
List<ContaCorrente> filtrados = dao.buscarPorCnpjCpf("44433322203");
Assert.assertEquals(2, filtrados.size());
assertCCEquals(contas.get(2), filtrados.get(0));
ContaCorrente novo1 = contas.get(2);
novo.setId(60L);
assertCCEquals(novo1, filtrados.get(0));
}

@After
public void commitTransaction() throws Exception {

utx.commit();
}

private void clearData() throws Exception {

utx.begin();
em.joinTransaction();
em.createQuery("delete from ContaCorrente").executeUpdate();
em.createQuery("delete from Agencia").executeUpdate();
utx.commit();
}

private void insertData() throws Exception {

utx.begin();
em.joinTransaction();
for (ContaCorrente cc : contas) {
em.persist(cc.getAgencia());
em.persist(cc);
}
utx.commit();
em.clear();
}

private void startTransaction() throws Exception {

utx.begin();
em.joinTransaction();
}

private void restaurarDados() {

List<ContaCorrente> ccs = new ArrayList<ContaCorrente>();
for (int i = 1; i <= 5; i++) {
ContaCorrente cc = new ContaCorrente();
cc.setAgencia(new Agencia());
cc.getAgencia().setCnpj("9988877700010" + i);
cc.getAgencia().setEndereco("Endereco agencia " + i);
cc.getAgencia().setId(5L * i);
cc.getAgencia().setNome("Nome agencia " + i);
cc.getAgencia().setNumero(100 * i);
cc.setCnpjCpf("4443332220" + i);
cc.setDigito(i);
cc.setId(10L * i);
cc.setNumero(1000 * i);
ccs.add(cc);
}
contas = Collections.unmodifiableList(ccs);
}

private static void assertCCEquals(ContaCorrente atual, ContaCorrente esperado) {

Assert.assertEquals(atual.getAgencia(), esperado.getAgencia());
Assert.assertEquals(atual.getAgencia().getCnpj(), esperado.getAgencia().getCnpj());
Assert.assertEquals(atual.getAgencia().getEndereco(), esperado.getAgencia().getEndereco());
Assert.assertEquals(atual.getAgencia().getNome(), esperado.getAgencia().getNome());
Assert.assertEquals(atual.getAgencia().getId(), esperado.getAgencia().getId());
Assert.assertEquals(atual.getAgencia().getNumero(), esperado.getAgencia().getNumero());
Assert.assertEquals(atual.getCnpjCpf(), esperado.getCnpjCpf());
Assert.assertEquals(atual.getDigito(), esperado.getDigito());
Assert.assertEquals(atual.getId(), esperado.getId());
Assert.assertEquals(atual.getNumero(), esperado.getNumero());
}
}
Listagem 7 – Teste DAO

Dependências

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>1.0.0.Final</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
</dependency>

<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Listagem 8 – Dependências

E adicione um profile:

<profiles>
<profile>
<id>arquillian-glassfish-embedded</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-glassfish-embedded-3.1</artifactId>
<version>1.0.0.CR3</version>
</dependency>

<dependency>
<groupId>org.glassfish.main.extras</groupId>
<artifactId>glassfish-embedded-web</artifactId>
<version>3.1.2</version>
</dependency>
</dependencies>
<build>
<testResources>
<testResource>
<directory>src/test/resources</directory>
</testResource>

<testResource>
<directory>src/test/resources-glassfish-embedded</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.config.file>
${project.build.testOutputDirectory}/logging.properties
</java.util.logging.config.file>
<derby.stream.error.file>
${project.build.directory}/derby.log
</derby.stream.error.file>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Listagem 9 – configurações Maven

Conclusão

Por utilizar o Glassfish Embarcado, obtêm-se testes em um ambiente JavaEE padrão do qual poderá ser implantado em qualquer outro servidor JavaEE e elimina-se a necessidade de instalações e configurações locais.

No próximo Post, por fim, será abordado como construir testes funcionais que executam dentro e fora do container e controlam os navegadores.

arquillian_ui_error_256px

Por IVAN RAFAEL MATINADO

Postado em: 20 de março de 2014

Confira outros artigos do nosso blog

REST não é JSON

21 de agosto de 2017

Bruno Sofiato

[Webinar] Profile de aplicações Java com Oracle Mission Control e Flight Recorder

24 de julho de 2017

Danival Calegari

Criando Mocks de serviços REST com SoapUI

27 de junho de 2017

Monise Costa

JavaScript 6: diferença entre var, let e const

09 de maio de 2017

Otávio Felipe do Prado

Deixe seu comentário