Arquillian Transaction Extension: Transaction Rollback for your Java EE Integration Tests

June 16th, 2013 by

I really love Arquillian to run integration tests for my Java EE applications – especially when running on different containers – and I also love the Arquillian tool stack from Arquillian Drone to the Arquillian Persistence Extensions.

Today I’d like to share a short snippet how to achieve transaction rollbacks when testing an EJB in combination with Arquillian and the Arquillian Transaction Extension…


 

Arquillian Basics

If you’ve never heard of the Arquillian framework before, please feel free to have a look at the Arquillian documentation or an article of mine: “Arquillian Tutorial: Writing Java EE 6 Integration Tests and more..“.

Dependencies / Setup

First of all we should add some necessary dependencies our project’s pom.xml:

Since I am using GlassFish as application container I need to add its dependencies org.glassfish.main.extras:glassfish-embedded-all and the Arquillian dependency for this specific container, org.jboss.arquillian.container:arquillian-glassfish-embedded-3.1.

A bunch of helpful Arquillian libraries is added by using the arquillian-bom artifact. To enable transaction support for the Arquillian the dependency arquillian-transaction-jta should be added to the project, too.

The other dependencies added here should be no surprise .. junit, hamcrest-matchers and a persistence provider – in this case EclipseLink…

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.jboss.arquillian</groupId>
			<artifactId>arquillian-bom</artifactId>
			<version>1.0.4.Final</version>
			<scope>import</scope>
			<type>pom</type>
		</dependency>
	</dependencies>
</dependencyManagement>
 
<dependencies>
	<dependency>
		<groupId>org.glassfish.main.extras</groupId>
		<artifactId>glassfish-embedded-all</artifactId>
		<version>3.1.2.2</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>org.jboss.arquillian.container</groupId>
		<artifactId>arquillian-glassfish-embedded-3.1</artifactId>
		<version>1.0.0.CR4</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.hamcrest</groupId>
		<artifactId>hamcrest-all</artifactId>
		<version>1.3</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.11</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.jboss.arquillian.junit</groupId>
		<artifactId>arquillian-junit-container</artifactId>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.jboss.arquillian.extension</groupId>
		<artifactId>arquillian-transaction-jta</artifactId>
		<version>1.0.0.Alpha3</version>
	</dependency>
	<dependency>
		<groupId>org.eclipse.persistence</groupId>
		<artifactId>eclipselink</artifactId>
		<version>2.5.0</version>
	</dependency>
</dependencies>
 
<repositories>
	<repository>
		<id>glassfish-repository</id>
		<url>http://download.java.net/maven/glassfish</url>
	</repository>
	<repository>
		<id>maven2-repository.dev.java.net</id>
		<name>Java.net Repository for Maven</name>
		<url>http://download.java.net/maven/2</url>
	</repository>
	<repository>
		<name>jboss-public</name>
		<id>jboss-public</id>
		<url>https://repository.jboss.org/nexus/content/repositories/</url>
	</repository>
	<repository>
		<id>EclipseLink Build Repo</id>
		<url>http://maven.eclipse.org/nexus/content/groups/build/</url>
	</repository>
</repositories>

JPA Setup

First we’re creating an entity with a minimal setup .. @Entity, @Id and getters/setters ..

package com.hascode.tutorial.entity;
 
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
 
@Entity
public class Book {
	@Id
	@GeneratedValue
	private Long id;
	private String title;
 
	public final Long getId() {
		return id;
	}
 
	public final void setId(final Long id) {
		this.id = id;
	}
 
	public final String getTitle() {
		return title;
	}
 
	public final void setTitle(final String title) {
		this.title = title;
	}
}

This is our persistence.xml in src/main/resources/META-INF .. we’re using the embedded database that is bundled by default with the GlassFish server and is exported as JNDI resource jdbc/__default:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
	<persistence-unit name="default" transaction-type="JTA">
		<provider>oracle.toplink.essentials.PersistenceProvider</provider>
		<jta-data-source>jdbc/__default</jta-data-source>
		<properties>
			<property name="eclipselink.ddl-generation" value="create-tables" />
		</properties>
	</persistence-unit>
</persistence>

Stateless Session Bean

We’re using an interface-less stateless session bean to provide access to the repository layer.

The layer simply allows us to persist book entities and to return all available books from the database.

package com.hascode.tutorial.ejb;
 
import java.util.List;
 
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
 
import com.hascode.tutorial.entity.Book;
 
@Stateless
public class BookEJB {
	@PersistenceContext
	private EntityManager em;
 
	public void save(final Book book) {
		em.persist(book);
	}
 
	public List<Book> findAll() {
		return em.createQuery("SELECT b FROM Book b ORDER BY b.title",
				Book.class).getResultList();
	}
}

Directory Structure

Now that we’re through the project’s directory structure might look like this one:

.
├── pom.xml
├── README.md
└── src
 ├── main
 │   ├── java
 │   │   └── com
 │   │       └── hascode
 │   │           └── tutorial
 │   │               ├── ejb
 │   │               │   └── BookEJB.java
 │   │               └── entity
 │   │                   └── Book.java
 │   ├── resources
 │   │   ├── beans.xml
 │   │   └── META-INF
 │   │       └── persistence.xml
 │   └── webapp
 │       └── WEB-INF
 └── test
 ├── java
 │   └── it
 │       ├── BookEJBIT.java
 │       └── TransactionalBookEJBIT.java
 └── resources

Normal Arquillian Integration Test

This is a normal Arquillian test. The first test persists a book, the second one persists two books adn tests if all available books are returned from the stateless session bean…

package it;
 
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
 
import java.util.List;
 
import javax.ejb.EJB;
 
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
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.hascode.tutorial.ejb.BookEJB;
import com.hascode.tutorial.entity.Book;
 
@RunWith(Arquillian.class)
public class BookEJBIT {
	@Deployment
	public static JavaArchive createDeployment() {
		return ShrinkWrap.create(JavaArchive.class)
				.addClasses(BookEJB.class, Book.class)
				.addAsResource("META-INF/persistence.xml")
				.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
	}
 
	@EJB
	BookEJB bookEJB;
 
	@Test
	public void shouldCreateASingleBook() throws Exception {
		Book book = new Book();
		book.setTitle("First book");
		bookEJB.save(book);
		assertThat(book.getId(), notNullValue());
 
		List<Book> books = bookEJB.findAll();
		assertThat(books.size(), equalTo(1));
		assertThat(books.get(0).getTitle(), equalTo("First book"));
	}
 
	@Test
	public void shouldReturnMultipleBooks() throws Exception {
		Book book1 = new Book();
		book1.setTitle("Some Book");
 
		Book book2 = new Book();
		book2.setTitle("Another book");
 
		bookEJB.save(book1);
		bookEJB.save(book2);
 
		List<Book> books = bookEJB.findAll();
		/**
		 * three because the test 'shouldCreateASingleBook' already created one
		 * book
		 */
		assertThat(books.size(), equalTo(3));
	}
}

Running both tests we should notice that the second test is affected by the state from the first test .. so one book already exists when the second test method is run ..

Arquillian test without transaction rollback

Arquillian test without transaction rollback

Transaction Rollback enabled Arquillian Test

Now the same test again but this time we’re using the transaction extension to enforce a transaction rollback on the first test.

This is done by adding an annotation @Transactional(TransactionMode.ROLLBACK) to the test method…

package it;
 
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
 
import java.util.List;
 
import javax.ejb.EJB;
 
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.transaction.api.annotation.TransactionMode;
import org.jboss.arquillian.transaction.api.annotation.Transactional;
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.hascode.tutorial.ejb.BookEJB;
import com.hascode.tutorial.entity.Book;
 
@RunWith(Arquillian.class)
@Transactional
public class TransactionalBookEJBIT {
	@Deployment
	public static JavaArchive createDeployment() {
		return ShrinkWrap.create(JavaArchive.class)
				.addClasses(BookEJB.class, Book.class)
				.addAsResource("META-INF/persistence.xml")
				.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
	}
 
	@EJB
	BookEJB bookEJB;
 
	@Test
	@Transactional(TransactionMode.ROLLBACK)
	public void shouldCreateASingleBook() throws Exception {
		Book book = new Book();
		book.setTitle("First book");
		bookEJB.save(book);
		assertThat(book.getId(), notNullValue());
 
		List<Book> books = bookEJB.findAll();
		assertThat(books.size(), equalTo(1));
		assertThat(books.get(0).getTitle(), equalTo("First book"));
	}
 
	@Test
	public void shouldReturnMultipleBooks() throws Exception {
		Book book1 = new Book();
		book1.setTitle("Some Book");
 
		Book book2 = new Book();
		book2.setTitle("Another book");
 
		bookEJB.save(book1);
		bookEJB.save(book2);
 
		List<Book> books = bookEJB.findAll();
		/**
		 * now its two .. because the test 'shouldCreateASingleBook' created one
		 * book but the transaction was rolled back.
		 */
		assertThat(books.size(), equalTo(2));
	}
}

When running the modified test now, we’ll see that the second test is not affected by the first test anymore ..

Arquillian test with transaction rollback

Arquillian test with transaction rollback

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/glassfish-continuous-deployment-tutorial.git

Resources

Tags: , , , , , , , ,

3 Responses to “Arquillian Transaction Extension: Transaction Rollback for your Java EE Integration Tests”

  1. chwali Says:

    Thanks, very helpful :)

  2. Bruno Says:

    Thanks for the guide!! I’ve tried with Wildfly 8.2.1 embedded and remote and it did not work. Maybe it only works with Glassfish.

  3. micha kops Says:

    Hi Bruno,

    do you receive an error or something like that? Only the transaction rollback does not work but running your integration tests with WildFly and Arquillian does not yield a problem?

Search
Tags
Categories