Testing enterprise applications that use persistence can be a hassle:
- A test database has to be configured.
- The database has to be seeded before the test.
- After the test the state of the database has to compared with the expected result.
This post shows how to write a test for a Java EE application that uses the Java Persistence API using these technologies:
- Arquillian is a testing platform that allows, among other things, testing Java EE applications in a Java EE application server.
- The Arquillian Persistence Extension is an extension to Arquillian that takes over seeding the database, matching the state of the database against the expected results and much more.
- Apache TomEE is a super lightweight open source Java EE application server that is certified for the Java EE Web Profile.
This article assumes that you have a basic knowledge of Java EE including JPA and Arquillian.
First let’s take a look at the test itself, how it becomes slimmer with the Arquillian Persistence Extension. Afterwards we will explore how to run these tests with the Arquillian Persistence Extension on TomEE.
Testing persistent applications
In this article we use a very simple entity MyEntity that only has two fields, key and value. That means our entity is a simple key value map:
src/main/java/org/superbiz/arqpersistence/MyEntity.java
@Entity(name = "MyEntity")
public class MyEntity {
@Id
@Column
private String key;
@Column
private String value;
public MyEntity() {}
public MyEntity(String key, String value) {
this.key = key;
this.value = value;
}
// Getters and setters omitted for brevity
}
Without the Arquillian Persistence Extension an Arquillian test that creates and updates one entity could look similar to the code below, which looks quite intimidating. For the sake of brevity the invocation of any real business logic is skipped and replaced by direct calls to the EntityManager
.
src/test/java/org/superbiz/arqpersistence/OldSchoolTest.java
@RunWith(Arquillian.class)
public class OldSchoolTest {
@Deployment
public static WebArchive deploy() throws Exception {
return ShrinkWrap.create(WebArchive.class, "OldSchoolTest.war")
.addClasses(MyEntity.class)
.addAsWebInfResource("test-persistence.xml", "persistence.xml");
}
@PersistenceContext(name = "myPU")
private EntityManager em;
@Resource
private UserTransaction trx;
@Test
public void shouldCreateEntity() throws Exception {
try {
// Given: Initial DB setup with empty tables
trx.begin();
em.createQuery("delete from MyEntity").executeUpdate();
trx.commit();
// When: an entity is created
trx.begin();
MyEntity myentity = new MyEntity("Some Key", "Some Value");
em.persist(myentity);
trx.commit();
// Then: only that one new entity is in the database
trx.begin();
MyEntity myEntity2 = em.find(MyEntity.class, "Some Key");
assertEquals("Some Value", myEntity2.getValue());
Number count = (Number) em.createQuery("select count(e) from MyEntity e").getSingleResult();
assertEquals(1, count.intValue());
trx.commit();
} finally {
// Rollback transaction in case it is open due to some error
if (trx.getStatus() == Status.STATUS_ACTIVE) {
trx.rollback();
}
}
}
@Test
public void shouldUpdateEntity() throws Exception {
try {
// Given: Initial DB setup with two entities
trx.begin();
em.createQuery("delete from MyEntity").executeUpdate();
em.persist(new MyEntity("Key 1", "Value 1"));
em.persist(new MyEntity("Key 2", "Value 2"));
trx.commit();
// When: one entity is updated
trx.begin();
MyEntity myentity = em.find(MyEntity.class, "Key 1");
myentity.setValue("Another Value 1");
trx.commit();
// Then: the two entities are still available and only one of them has the new value
trx.begin();
assertEquals("Another Value 1", em.find(MyEntity.class, "Key 1").getValue());
assertEquals("Value 2", em.find(MyEntity.class, "Key 2").getValue());
Number count = (Number) em.createQuery("select count(e) from MyEntity e").getSingleResult();
assertEquals(2, count.intValue());
trx.commit();
} finally {
// Rollback transaction in case it is open due to some error
if (trx.getStatus() == Status.STATUS_ACTIVE) {
trx.rollback();
}
}
}
}
The method deploy()
prepares the test deployment. There is not much to add besides the entity class and a persistence.xml
. We chose an own test-persistence.xml
so that we can tell the EntityManager
to create the schema itself.
We get the EntityManager
injected into the test case and we also need a UserTransaction
to prepare and check the database in separate transactions.
The test shouldCreateEntity()
does nothing but creating an entity. Before the test it has to be ensured that the database is clean. Afterwards it has to be checked that this and only this entity is in the database.
The test shouldUpdateEntity()
updates one entity when two entities are in the database. Before the test the two entities have to be created in the database. After the test the result has to be verified that still two entities exist and one of them has the new value.
Noticed how much code is spent on preparation of the database and testing of the results? And in particular testing the results becomes more and more complex the more entities are used or you decide to skip some checks.
Now let’s see what the test looks like when we use the Arquillian Persistence Extension. In contrast to the first example it will do seeding of the database, verification of the results and transaction handling automatically for us!
src/test/java/org/superbiz/arqpersistence/PersistenceTest.java
@RunWith(Arquillian.class)
public class PersistenceTest {
@Deployment
public static WebArchive deploy() throws Exception {
return ShrinkWrap.create(WebArchive.class, "PersistenceTest.war")
.addClasses(MyEntity.class)
.addAsWebInfResource("test-persistence.xml", "persistence.xml");
}
@PersistenceContext(name = "myPU")
private EntityManager em;
@Test
@InSequence(1)
public void initEntityManager() {
em.getMetamodel();
}
@Test
@ShouldMatchDataSet("datasets/after_create.xml")
@InSequence(2)
public void shouldCreateEntity() throws Exception {
MyEntity myentity = new MyEntity("Some Key", "Some Value");
em.persist(myentity);
}
@Test
@UsingDataSet("datasets/before_update.xml")
@ShouldMatchDataSet("datasets/after_update.xml")
@InSequence(2)
public void shouldUpdateEntity() throws Exception {
MyEntity myentity = em.find(MyEntity.class, "Key 1");
myentity.setValue("Another Value 1");
}
}
The method deploy()
prepares the test deployment just the same way as the initial test.
We also use the EntityManager
in our test, so we get it injected from Arquillian. In contrast to the first test we don’t need a UserTransaction
. The Arquillian Persistence Extension automatically executes every test in its own transaction.
If you look at the test initEntityManager()
you actually see a workaround for one of the traps you can step in when using OpenJPA, as is included in TomEE. OpenJPA does not create the schema until the EntityManager
is initialized. As the Arquillian Persistence Extension does not use JPA to seed and check the database, we have to ensure that the schema is created before the first “real” persistence test. One way to make OpenJPA create the schema is to invoke the method EntityManager.getMetamodel()
. The @InSequence
annotation ensures that this test method is invoked first. Notice that you don’t need this if you use Hibernate as your JPA provider.
In our first test that creates an entity we don’t seed the database, Arquillian Persistence Extension will automatically clean it for us before every test. We describe the expected outcome of the test in the file datasets/after_create.xml
referenced by the annotation @ShouldMatchDataSet
. Arquillian Persistence Extension will automatically compare the database contents with this file. We will show the structure of a dataset below. The annotation @InSequence(2)
will guarantee that this method is executed after initEntityManager()
.
In our update test we also define the initial dataset defined in the file datasets/before_update.xml
that is referenced by the annotation @UsingDataSet
.
The datasets mentioned above can be anything that is accepted by DBUnit like XML or JSON.
The initial dataset for the update test could look like this:
datasets/before_update.xml
<dataset>
<myentity key="Key 1" value="Value 1"/>
<myentity key="Key 2" value="Value 2"/>
</dataset>
The expected dataset looks like this:
datasets/after_update.xml
<dataset>
<myentity key="Key 1" value="Another Value 1"/>
<myentity key="Key 2" value="Value 2"/>
</dataset>
Did you notice how much code just disappeared?
The remaining problems we have are executing the tests on the application server and accessing a database from the tests, but first we have to build the project.
Setting up the project
We use Maven, so this section explains how to setup the Maven build. First we add the dependency on the JavaEE API:
<dependencies>
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0-6</version>
<scope>provided</scope>
</dependency>
</dependencies>
Next we have to define the dependencies on our testing framework and on Arquillian. We add the awesome new arquillian-universe
bom to the dependency management section. This bom ties together the versions of the Arquillian core as well as all standard extensions, including the Persistence Extension. As we use JUnit as our test framework we add a dependency on junit and on the integration for Arquillian in JUnit, arquillian-junit-container
.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.arquillian</groupId>
<artifactId>arquillian-universe</artifactId>
<version>1.0.0.Alpha1</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
To use the Arquillian Persistence Extension and make sure that the tests are executed in a transaction we add the dependencies on the persistence and transaction extension. Note that we don’t define any versions, as these are already defined by the Arquillian universe bom.
<dependencies>
<dependency>
<groupId>org.arquillian.universe</groupId>
<artifactId>arquillian-persistence</artifactId>
<scope>test</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.arquillian.universe</groupId>
<artifactId>arquillian-transaction-jta</artifactId>
<scope>test</scope>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-transaction-jta</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
The dependency on arquillian-persistence
gives access to the database seeding and result matching via the annotations @UsingDataSet
and @ShouldMatchDataSet
.
The dependency on arquillian-transaction-jta
allows us to execute the tests within a JTA transaction so that the test does not have to begin and commit transaction itself.
The exclusion of the transitive dependency on arquillian-transaction-jta
is one specific point to keep in mind. The TomEE Arquillian adapter brings its own implementation of the service brought by this artifact. Unfortunately it can be very hard to diagnose this problem.
Finally we add the dependency on the remote TomEE container adapter. What is really awesome about the TomEE container adapter is that you have a remote container, that is started and stopped by Arquillian, but you don’t need to install it anywhere. The container adapter will automatically download the zip, extract it and run the container from there.
<dependencies>
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>arquillian-tomee-remote</artifactId>
<version>1.7.3</version>
</dependency>
</dependencies>
Configuring and executing the test
There are essentially three things that you have to configure:
- We have to add a
persistence.xml
for the test that makes OpenJPA create the database schema, assuming that the default version will not. - Then we have to configure the remote TomEE instance so that it can provide the DataSource for the
EntityManager
as well as for the Arquillian Persistence Extension. As we want to have our tests as lightweight as possible we use an HSQL in memory datasource, so that there is nothing to install or anything that survives two consecutive tests. Fortunately, TomEE already contains the JDBC driver for it in its lib/ directory. - Finally we have to tell the Arquillian Persistence Extension how to get a connection to the database for seeding and checking.
The test-persistence.xml simply looks like this:
src/test/resources/test-persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence 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"
version="2.0">
<persistence-unit name="myPU" transaction-type="JTA">
<jta-data-source>fooDS</jta-data-source>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings"
value="buildSchema(ForeignKeys=true)"/>
<property name="openjpa.Log"
value="DefaultLevel=TRACE,SQL=TRACE" />
</properties>
</persistence-unit>
</persistence>
fooDS
is the name of the javax.sql.DataSource
the EntityManager
wants to use. In TomEE this is simply the name of the resource.
Via the property openjpa.jdbc.SynchronizeMappings
with the value buildSchema(ForeignKeys=true)
we make OpenJPA create the schema at initialization time including creation of foreign key constraints.
Via this property we activate logging for OpenJPA so that we can see what it does during the test.
The arquillian.xml
configures the TomEE instance as well as the Arquillian Persistence Extension:
src/test/resources/arquillian.xml
<?xml version="1.0"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://jboss.org/schema/arquillian
http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<container qualifier="server" default="true">
<configuration>
<property name="httpsPort">-1</property>
<property name="httpPort">-1</property>
<property name="stopPort">-1</property>
<property name="ajpPort">-1</property>
<property name="simpleLog">true</property>
<property name="cleanOnStartUp">true</property>
<property name="dir">target/server</property>
<property name="appWorkingDir">target/arquillian</property>
<property name="properties">
fooDS = new://Resource?type=DataSource
fooDS.JdbcDriver = org.hsqldb.jdbcDriver
fooDS.JdbcUrl = jdbc:hsqldb:mem:arquillian
fooDS.UserName = sa
fooDS.Password =
fooDS.JtaManaged = true
fooDS.LogSql = true
</property>
<property name="debug">false</property>
</configuration>
</container>
<extension qualifier="persistence">
<property name="defaultDataSource">openejb:Resource/fooDS</property>
</extension>
</arquillian>
The properties in the property “property” of the container element configure the HSQL datasource under the name fooDS
. Notice that the lookup name for this resource for the Arquillian Persistence Extension will be openejb:Resource/fooDS
.
In case you want to debug the application in the server during a test the “debug” property is there to help. It will make the TomEE Arquillian container adapter start the server process waiting for a Debugger on port 5005.
The property “defaultDataSource” configures the lookup name of the DataSource that the Arquillian Persistence Extension uses to seed and check the database. This has to be configured appropriately. The extension supports a whole bunch of other properties that have reasonable defaults. You can find a description of all properties at the Arquillian Persistence Extension documentation.
Finally you can simply run the test via mvn clean test
.
Conclusion
Arquillian, Arquillian Persistence Extension and TomEE are a perfect match for testing applications that use persistence. You get a test environment including a database without any prior configuration of the system.
Nevertheless, there are some points you have to pay attention to when running on TomEE and OpenJPA , which are:
- Exclude the transitive dependency
org.jboss.arquillian.extension:arquillian-transaction-jta
. - Initialize the
EntityManager
before the Arquillian Persistence Extension seeds the database the first time. - Configure the correct lookup name for the Arquillian Persistence Extension via the
defaultDataSource
property of the persistence extension configuration section.
If you want to have a look at the full sources of this example you can simply clone the project at GitHub.
If you want to read more about how much fun testing with Arquillian on TomEE can be you have to read the excellent book Arquillian in Action by Alex Soto Bueno and Jason Porter.