March 29th, 2010Testing JPA with JUnit, Spring and Maven
Problem
Majority of enterprise applications use database as a persistence layer. JPA framework allows developers to manage relational data in their J2SE and J2EE applications. While knowledge about JPA domain modeling techniques is widespread and available, there is little known about persistence layer testing. In this post I will describe the process of setting up an environment for an application integration testing using JUnit, Spring Framework and Maven.
The standard Maven 2 directory layout
The standard Maven 2 directory structure can be seen below.

In the project home directory goes the pom.xml and two subdirectories – src for source code and target for compilation output. The src directory has two subdirectories, main – all files related to the application being developed go here and test – all test related files go here.
Naturally, this layout might be different, most likely having more elements – this depends on the archetype used during project inception, but, nevertheless, the layout above is an absolute minimum. The test directory have two subdirectories – java – with all unit tests and resources – with resources, which are used for testing, but are not deployed.
Having two separate directories for application and test allows us to have two, completely separated configuration files, so that testing environment was completely independent from development environment. It is possible, to develop using one database instance and test on the other, or even use MySQL for development and HSQLDB for testing, if required.
Configuration files
persistence.xml
The persistence.xml file defines a persistence-unit, and according to specification, has to be included an the META-INF directory of a JAR file that contains entities. However, Spring gives a little bit more room here and my approach is to put it in a config subdirectory of test/resources. The minimum persistence.xml:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="persistenceUnit" transaction-type="RESOURCE_LOCAL"/>
</persistence>
defines only the persistence-unit name, everything else is configured in Spring Framework configuration file – applicationContext.xml, which goes also to a config directory.
applicationContext.xml
Spring Framework configuration file – applicationContext.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
">
<!-- Enable processing of PersistenceUnit and PersistenceContext annotations -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<!-- Persistence exception translation for Repositories -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- Enable annotation-driven configuration and auto-detection -->
<context:annotation-config/>
<!-- Enable component scan -->
<context:component-scan base-package="com.bewareofthebear"/>
<!-- Configure property placeholders for environment-specific properties customization -->
<context:property-placeholder location="classpath:/properties/*.properties" />
<!-- Datasource to be used by Hibernate/JPA -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="${db.driverClassName}"
p:url="${db.url}"
p:username="${db.username}"
p:password="${db.password}"
/>
<!-- EntityManagerFactory using defined datasource and JPA provider - Hibernate -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:persistenceXmlLocation="classpath:/config/persistence.xml"
p:dataSource-ref="dataSource"
p:persistenceUnitName="persistenceUnit"
>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.use_sql_comments">false</prop>
</props>
</property>
</bean>
<!-- Transaction manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"
p:dataSource-ref="dataSource"
/>
<!-- Use @Transactional annotation on methods -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
As seen, using persistenceXmlLocation property of EntityManagerFactory I was able to point to my test version of a persistence.xml file. This approach has however one drawback which comes from limitations of JPA’s entities scan. It scans only paths within a persistent unit root url, which is a parent folder of persistence.xml location. Since maven copies content of the resource folder to the target/test-classes folder, than this folder becomes the root url and is scanned during application start-up. As a result of this, all application entities defined under main/java/… will not be scanned and, if test database is different than development database, than tests will fail. There are few ways to solve this problem.
First, it is possible to use an application version of persistence.xml instead of test version, however, this approach has the same drawback, just in opposite direction – all application entities are scanned, but, if there are any entities defined in a test directory than these will not be scanned.
Second solution is to explicitly list required entities in a persistence.xml file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<class>com.bewareofthebear.domain.model.customer.Customer</class>
<class>com.bewareofthebear.domain.model.customer.Category</class>
</persistence-unit>
</persistence>
Which works, but could be time consuming and error prone.
Third solution is to use PersistenceUnitPostProcessors, which will scan all entities and add them to persistence unit.
package com.bewareofthebear.application;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import org.scannotation.AnnotationDB;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
public class TestPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
private final Logger log = LoggerFactory.getLogger( TestPersistenceUnitPostProcessor.class );
public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
URL persistenceUnitRootUrl = pui.getPersistenceUnitRootUrl();
String protocol = persistenceUnitRootUrl.getProtocol();
String host = persistenceUnitRootUrl.getHost();
int port = persistenceUnitRootUrl.getPort();
String file = persistenceUnitRootUrl.getFile();
String newFile = file.substring(0, file.lastIndexOf("test-classes")) + "classes/";
URL url = null;
try {
url = new URL(protocol, host, port, newFile);
} catch (MalformedURLException e) {
log.warn( "Malformed url: " + url, e );
return;
}
AnnotationDB annotationDB = new AnnotationDB();
try {
annotationDB.scanArchives(url);
} catch (IOException e) {
log.warn( "Unexpected IOException", e );
return;
}
if (annotationDB.getAnnotationIndex().containsKey("javax.persistence.Entity")) {
for (String entity : annotationDB.getAnnotationIndex().get("javax.persistence.Entity")) {
pui.addManagedClassName(entity);
log.debug( "Adding: {}", entity );
}
}
}
}
PersistenceUnitPostProcessors has to be wired to LocalContainerEntityManagerFactoryBean:
<property name="persistenceUnitPostProcessors">
<list><bean class="com.bewareofthebear.application.TestPersistenceUnitPostProcessor"/></list>
</property>
Now, the configuration is completed, so it is time to move on and create some tests.
Test classes
The basic template for creating test classes:
package com.bewareofthebear.test;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/config/applicationContext.xml"})
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
@Transactional
public class JPATestTemplate {
@BeforeTransaction
public void verifyInitialDatabaseState() {
// logic to verify the initial state before a transaction is started
}
@Before
public void setUpTestDataWithinTransaction() {
// set up test data within the transaction
}
@Test
@Rollback(true)
public void modifyDatabaseWithinTransaction() {
// logic which uses the test data and modifies database state
}
@After
public void tearDownWithinTransaction() {
// execute "tear down" logic within the transaction
}
@AfterTransaction
public void verifyFinalDatabaseState() {
// logic to verify the final state after transaction has rolled back
}
}
- @RunWith(SpringJUnit4ClassRunner.class) – tells JUnit to use Spring’s own built-in Test Runner that is aware of Spring-specific annotations
- @ContextConfiguration(locations={“classpath:/config/applicationContext.xml”}) – creates a Spring context for this test from the specified context file.
- @Transactional – before the method is executed a transaction is opened, upon exit the transaction is commited unless an exception occurs in which case it is rolled back.
- @TransactionConfiguration(transactionManager=”transactionManager”, defaultRollback=false) – defines class-level metadata for configuring transactional tests
- @Rollback(true) – forces the database to rollback this transaction when the test terminates regardless of whether the test fails, an exception is thrown or not.
Sample usage:
package com.bewareofthebear.test;
import static org.junit.Assert.assertEquals;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
import com.bewareofthebear.domain.model.customer.Customer;
import com.bewareofthebear.infrastructure.persistence.CustomerDao;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/config/applicationContext.xml"})
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
@Transactional
public class JPATestCustomer {
@Autowired
CustomerDao customerDao;
@Before
public void setUpTestDataWithinTransaction() {
Customer customer = null;
customer = new Customer ();
customer.setFirstName("John"); customer.setFirstName("Doe");
customerDao.persist(customer);
customer = new Customer ();
customer.setFirstName("Jack"); customer.setFirstName("Rabbit");
customerDao.persist(customer);
customer = new Customer ();
customer.setFirstName("Richard "); customer.setFirstName("Roe");
customerDao.persist(customer);
}
@Test
@Rollback(true)
public void countTest() {
Long count = customerDao.countAll();
assertEquals(count, new Long(3));
}
@Test
@Rollback(true)
public void findAllTest() {
List<Customer> all = customerDao.findAll();
assertEquals(all.size(), 3);
}
}
Complete project
complete sources used during writing.




