Arquillian Transaction Extension: Transaction Rollback for your Java EE Integration Tests
June 16th, 2013 by Micha KopsI 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…
Contents
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 ..
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 ..
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
- Arquillian Project Website
- Arquillian Transaction Extension Project Website
- Transaction Extension on GitHub
Tags: arquillian, ejb, java ee, jee, jpa, persistence, rollback, testing, transaction
January 7th, 2014 at 1:42 pm
Thanks, very helpful :)
July 29th, 2015 at 3:39 am
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.
July 29th, 2015 at 4:59 am
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?