Testing JEE6 applications with Arquillian

Creating automated tests for JEE applications has always been a challenge, once it requires a running container to provide all the services the application needs to work properly and that the tests need to be executed.

The Arquillian framework was born to make this job a lot easier to be executed. Your main goal is to provide a powerful test platform responsible of taking care of many aspects while testing JEE applications, such as start a container, deploy an application in the container, run the tests and stop the container. It also allows that your test classes can be enriched with annotations like @Inject, @EJB and @Resource, among others. This way, one can easily create real integration tests. Arquillian also allows users to choose either JUnit or TestNG to create and execute the tests, besides allowing the same test cases to be executed in different containers, such as JBoss, Glassfish and Tomcat.

This post’s goal is not to discuss the benefits in writing automated tests -TDD or not- but, instead, focus on how to use Arquillian through a practical example.

Demo application with Arquillian

In order to show how Arquillian works, we’ll be creating a simple JEE module responsible for registering and finding customers. Maven will be used as the tool responsible to create the package and manage dependencies. We’ll be also using JBoss 7.1.1-final as application server, JUnit 4.8.2 to write/run tests and the HSQLDB as the database, that already comes shipped and configured with JBoss.

The link to the source code on GitHub is available at the end of this page.

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

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

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

	<!-- Sets the profile responsible for running the Arquillian tests -->
	<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>

This pom.xml is fairly simple, but it’s worth to highlight the creation of the “jbossas-7” profile, that is responsible for defining the dependencies needed to execute the Arquillian test cases. The first dependency imports the Arquillian’s core library, that holds the required classes and annotations to create a test case. The second one imports the module responsible for running the test cases against JBoss 7.1.1-Final, that will be executed in the “Managed” mode. As our test cases will only reference the classes provided by the arquillian-core library, we can run the same test cases in different containers, without changing the code. Within Arquillian’s documentation, you can find all the supported containers and the details regarding the 3 execution modes: remote, managed and embedded.
In our example we’re using the managed mode, which means that Arquillian will be responsible for starting and stoping JBoss, besides performing the application’s deployment/undeployment. It’s necessary that you have JBoss installed in your machine and that the JBOSS_HOME environment variable is properly set.

Main code (System under test)

The application we’ll build to be tested is simple and is comprised by:

  • A JPA entity called Customer
  • An EJB local interface with the business methods
  • A Stateless Session Bean that implements the local interface
  • A Stateless Session Bean acting as the DAO

The java code is quite basic and doesn’t require further explanations.

Customer entity – Customer.java
package com.lucianomolinari.arquillian_example.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 = "customer")
public class Customer implements Serializable {
	private static final long serialVersionUID = 7796759140893817571L;

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

	private String name;

	public Customer() {
	}

	public Customer(String name) {
		this.name = name;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Customer other = (Customer) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Customer [ID: " + id + ";Name: " + name + "]";
	}

}
Local business interface – CustomerServices.java
package com.lucianomolinari.arquillian_example.services;

import java.util.List;

import javax.ejb.Local;

import com.lucianomolinari.arquillian_example.entity.Customer;

@Local
public interface CustomerServices {

	Customer add(Customer customer);

	List<Customer> findAll();

}
Dao – CustomerDao.java
package com.lucianomolinari.arquillian_example.dao;

import java.util.List;

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

import com.lucianomolinari.arquillian_example.entity.Customer;

@Stateless
public class CustomerDao {

	@PersistenceContext
	private EntityManager em;

	public Customer add(Customer customer) {
		em.persist(customer);
		return customer;
	}

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

}
Local interface’s implementation – CustomerServicesImpl.java
package com.lucianomolinari.arquillian_example.services.impl;

import java.util.List;

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

import com.lucianomolinari.arquillian_example.dao.CustomerDao;
import com.lucianomolinari.arquillian_example.entity.Customer;
import com.lucianomolinari.arquillian_example.services.CustomerServices;

@Stateless
public class CustomerServicesImpl implements CustomerServices {

	@Inject
	private CustomerDao customerDao;

	public Customer add(Customer customer) {
		return customerDao.add(customer);
	}

	public List<Customer> findAll() {
		return customerDao.findAll();
	}

}

An empty file named beans.xml must be created inside src/main/resources/META-INF.

The test side

It’s comprised by a test class and by the file test-persistence.xml that should be placed inside 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>
TestCustomerServices.java
package com.lucianomolinari.arquillian_example.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.lucianomolinari.arquillian_example.entity.Customer;

@RunWith(Arquillian.class)
public class TestCustomerServices {

	@Inject
	private CustomerServices customerServices;

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

	@Test
	public void testAddAndFindCustomers() {
		List<Customer> customers = customerServices.findAll();

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

		customerServices.add(new Customer("Customer 1"));
		customerServices.add(new Customer("Customer 2"));

		customers = customerServices.findAll();
		assertNotNull(customers);
		assertEquals(2, customers.size());
		assertEquals("Customer 1", customers.get(0).getName());
		assertEquals("Customer 2", customers.get(1).getName());
	}

}

This is the most important part of this post, so let’s walk through the code:

The required @RunWith(Arquillian.class) annotation tells JUnit that this test class should be run by Arquillian. Pay attention on the use of @Inject, it’s the same as you were using it in a real code, with not difference. Any kind of classes can be injected, such as CDI beans, EJBs and Resources.

In order to execute the test cases, Arquillian gets all the code, packs it in a deployable package and deploys it on the server. In order to do that, the package type must be defined along with the classes and files that must be part of this package. This is done through a public static method annotated with @Deployment and that returns the type of the package to be created. There are 4 ways: JavaArchive, EnterpriseArchive, ResourceAdapterArchive and WebArchive. We’re using JavaArchive in our example.

The following tasks are performed within this method:

  • Arquillian relies on the ShrinkWrap library to create the Java package at runtime
  • In the first line of code the type and name of the package are specified
  • The second line defines which classes will be packed. In this example, the package com.lucianomolinari.arquillian_example and its sub-packages are imported recursively. That’s possible due to the true value informed in the first parameter, which says that the importing should be recursive. The methods addClass() and addClasses() can also be used.
  • On the third line it’s informed to create an empty file named beans.xml and add it as a manifest resource. This file is required by CDI.
  • The test-persistence.xml file is added on the last line as a manifest resource named persistence.xml. That’s really useful, as one can have a persistence.xml within src/main/resources/META-INF to be used in production and another (in this case src/test/resources/test-persistence.xml) to be used during tests execution. It’s worth to remember that this package has no relation with the real artifact, as this is a simple .jar used to run the test cases.

In order to write the test cases, JUnit is used normally. See that there’s no mock in this test, everything is tested insided the container.

IDE

In order to configure Arquillian inside your preferred IDE, take a look in the Getting started guide.

Running the tests through command line

In order to run the tests through the command line, the following command should be performed:
mvn test -Pjbossas-7

In this way, the tests will be run using the jbossas-7 profile, configure above inside pom.xml.

Conclusion

Creating and executing automated tests is a must do task on software development. The more real the tests are, more efficient they’ll be. Arquillian addresses exactly this point in making the tests more real, besides making it easier to create tests for JEE applications.

The source-code can be seen on GitHub. Any questions? Please feel free to use the comments section here in the blog or contact me via twitter.

11 thoughts on “Testing JEE6 applications with Arquillian

  1. 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.

    Like

  2. 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?

    Like

Leave a comment