Unit Testing Maven Based JPA Application on GAE


Recently, we started porting a complex enterprise timesheet and invoicing application to Google App Engine. We would talk about the strategy that we are following as a part of separate post but in this post let us look at how we can unit test JPA code effectively in our local environment.

By the way, the application that we are porting does not use JPA but it does use Spring. So we can make use of some of the Spring capabilities. The app engine does not store data in a relational format, however, it does support JPA 1.0. So we converted a simple Dao to JPADao and wanted to test it locally.

For local testing, you need some jars in the classpath which simulates the local runtime APIs for your tests. GAE. The jars that are required to be included in your pom.xml for local testing are

		<!--  Unit testing Jars -->
		<dependency>
			<groupid>com.google.appengine</groupid>
			<artifactid>appengine-testing</artifactid>
			<version>1.3.4</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupid>com.google.appengine</groupid>
			<artifactid>appengine-api-labs</artifactid>
			<version>1.3.4</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupid>com.google.appengine</groupid>
			<artifactid>appengine-api-stubs</artifactid>
			<version>1.3.4</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupid>junit</groupid>
			<artifactid>junit</artifactid>
			<version>4.8.1</version>
			<scope>test</scope>
		</dependency>

Now, you would notice that some/most of these jars are not available on the central maven repository or other repositories. So you would have to include them in your local repository or nexus if you have one.

You could include them one by one or using a shell script like this

#!/bin/sh

#
# This script installs the Google App Engine artifacts into you local Maven repository.
#
# If you would like to avoid the need for this step, ask Google by voting for the following issue:
#   http://code.google.com/p/googleappengine/issues/detail?id=1296
#

if [ "$1" == "" ]
then
    echo "usage: $0 /path/to/appengine-java-sdk-1.3.4"
    exit 1
fi

GAE_SDK_PATH=$1

mvn install:install-file -Dfile=$GAE_SDK_PATH/lib/user/appengine-api-1.0-sdk-1.3.4.jar -DgroupId=com.google.appengine -DartifactId=appengine-api-1.0-sdk -Dversion=1.3.4 -DgeneratePom=true -Dpackaging=jar

mvn install:install-file -Dfile=$GAE_SDK_PATH/lib/user/appengine-api-labs-1.3.4.jar -DgroupId=com.google.appengine -DartifactId=appengine-api-labs -Dversion=1.3.4 -DgeneratePom=true -Dpackaging=jar

mvn install:install-file -Dfile=$GAE_SDK_PATH/lib/impl/appengine-api-stubs.jar -DgroupId=com.google.appengine -DartifactId=appengine-api-stubs -Dversion=1.3.4 -DgeneratePom=true -Dpackaging=jar

mvn install:install-file -Dfile=$GAE_SDK_PATH/lib/testing/appengine-testing.jar -DgroupId=com.google.appengine -DartifactId=appengine-testing -Dversion=1.3.4 -DgeneratePom=true -Dpackaging=jar

mvn install:install-file -Dfile=$GAE_SDK_PATH/lib/user/orm/datanucleus-appengine-1.0.7.final.jar -DgroupId=com.google.appengine -DartifactId=datanucleus-appengine -Dversion=1.0.7 -DgeneratePom=true -Dpackaging=jar

mvn install:install-file -Dfile=$GAE_SDK_PATH/lib/user/orm/geronimo-jta_1.1_spec-1.1.1.jar -DgroupId=com.google.appengine -DartifactId=geronimo-jta_1.1_spec -Dversion=1.1.1 -DgeneratePom=true -Dpackaging=jar

If you refer to the GAE JPA documentation, another important step which has to be done is the enhancement of data classes. The DataNucleus implementation of JPA uses a post-compilation “enhancement” step in the build process to associate data classes with the JPA implementation.

Luckily, we have a maven plugin called maven-datanucleus-plugin which does the enhancement for us whenever any compilation takes place. Place that in your pom.xml

<plugin>
				<groupid>org.datanucleus</groupid>
				<artifactid>maven-datanucleus-plugin</artifactid>
				<version>1.1.4</version>
				<configuration>
					<mappingincludes>**/domain/*.class</mappingincludes>
					<verbose>true</verbose>
					<enhancername>ASM</enhancername>
					<api>JPA</api>
				</configuration>
				<executions>
					<execution>
						<phase>compile</phase>
						<goals>
							<goal>enhance</goal>
						</goals>
					</execution>
				</executions>
				<dependencies>
					<dependency>
						<groupid>org.datanucleus</groupid>
						<artifactid>datanucleus-core</artifactid>
						<version>1.1.5</version>
						<exclusions>
							<exclusion>
								<groupid>javax.transaction</groupid>
								<artifactid>transaction-api</artifactid>
							</exclusion>
						</exclusions>
					</dependency>
					<dependency>
						<groupid>org.datanucleus</groupid>
						<artifactid>datanucleus-rdbms</artifactid>
						<version>1.1.5</version>
					</dependency>
					<dependency>
						<groupid>org.datanucleus</groupid>
						<artifactid>datanucleus-enhancer</artifactid>
						<version>1.1.4</version>
					</dependency>
				</dependencies>
			</plugin>

Also include the relevant repositories for fetching this plugin.

Now our infrastructure for writing local tests is complete.

Let us look at two ways now for writing the unit tests.
One – Without using Spring
If your application does not use spring then you cannot use the JpaDaoSupport from spring. Here you should be able to use the EntityManager directly. So for instance you would have the EMF.java like this

public final class EMF {
	private static final EntityManagerFactory emfInstance = Persistence
			.createEntityManagerFactory("transactions-optional");

	private EMF() {
	}

	public static EntityManagerFactory get() {
		return emfInstance;
	}
}

And your tests would extend from LocalDatastoreTestCase

public abstract class LocalDatastoreTestCase extends TestCase {

	private final LocalServiceTestHelper helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());

	@Before
	public void setUp() {
		helper.setUp();
	}

	@After
	public void tearDown() {
		helper.tearDown();
	}
}

Now, the magic here happens in the the setUp method of the LocalServiceTestHelper which sets up the local runtime environment. LocalServiceTestHelper is setting up and tearing down the parts of the execution environment that are common to all local services, and LocalDatastoreServiceTestConfig is setting up and tearing down the parts of the execution environment that are specific to the local Datastore service.

Your test case could look like this

public class EmployeeDaoTest extends LocalDatastoreTestCase {

	@Test
	public void testShouldPersistEmployee() {
		EntityManager em = EMF.get().createEntityManager();
		Employee employee = new Employee();
		employee.setFirstName("Vikas");
		employee.setLastName("Hazrati");
		employee.setHireDate(new Date());

		em.persist(employee);

		Employee merge = em.merge(employee);

		Employee foundEmployee = em.find(Employee.class, merge.getKey());

		Assert.assertEquals(employee.getFirstName(), foundEmployee.getFirstName());

	}

Two – With Spring
If you are using Spring then you do not need an explicit handle on the EntityManager. The base test case could look like this

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class })
@ContextConfiguration(locations = { "classpath:test-applicationContext.xml", "classpath:test-applicationContext-dao.xml"})
public abstract class LocalDatastoreSpringTestCase extends TestCase {

	private final LocalServiceTestHelper helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());

	@Before
	public void setUp() {
		helper.setUp();
	}

	@After
	public void tearDown() {
		helper.tearDown();
	}
}

and the corresponding test would look like a regular JPA based test within spring

public class EmployeeDaoSpringTest extends LocalDatastoreSpringTestCase {

	@Autowired
	private EmployeeDao employeeDao;

	@Test
	public void testShouldPersistEmployee() {
		Employee employee = new Employee();
		employee.setFirstName("Vikas");
		employee.setLastName("Hazrati");
		employee.setHireDate(new Date());

		employeeDao.createEmployee(employee);

		Collection<employee> list = employeeDao.list();

		Assert.assertEquals(1, list.size());
	}

Thus, once you set up the basic infrastructure, which is a bit tricky considering the dependencies which have to be included in your pom.xml, the actual tests are similar to what you have been writing for testing DAOs.
The entire code for the sample application is present here, on google code for your reference.

About Vikas Hazrati

Vikas is the Founding Partner @ Knoldus which is a group of software industry veterans who have joined hands to add value to the art of software development. Knoldus does niche Reactive and Big Data product development on Scala, Spark and Functional Java. Knoldus has a strong focus on software craftsmanship which ensures high-quality software development. It partners with the best in the industry like Lightbend (Scala Ecosystem), Databricks (Spark Ecosystem), Confluent (Kafka) and Datastax (Cassandra). To know more, send a mail to hello@knoldus.com or visit www.knoldus.com
This entry was posted in Java and tagged , , , . Bookmark the permalink.

8 Responses to Unit Testing Maven Based JPA Application on GAE

  1. Hello Vikas,

    I would say you can use gae maven plugin http://code.google.com/p/maven-gae-plugin/.
    This way you can mavenify your project and you don’t need to manually install the jars.

    Thanks
    Shekhar

  2. Hi Shekhar,
    Thanks for your suggestion. I did try the maven gae plugin. Somehow did not get a great feeling about the project that it created. It was also looking for local runtime environment, which does not come post 1.3.4 I guess. May be it would be good to have another look at it and see that I did not goof up elsewhere.

  3. Ben says:

    Thanks for posting this — it has been really helpful. Is the source code available somewhere? You mentioned it being available on Google Code, but I wasn’t able to locate it.

    Thanks!
    -Ben

  4. Hi Ben, Let me see if I can get the code and post the link here.

  5. lucio says:

    hi there,

    i cant reproduce the unit test with spring.
    can u post your spring config to see what i have done wrong.

    thanks

    -lp

  6. Hi Lucio,

    I tried to dig the code and this is what I could get, I hope it helps,

    ##### test-applicationContext.xml

    &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
    &lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    	xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
    	xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot;
    	xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.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-2.0.xsd&quot;&gt;
    
    	&lt;tx:annotation-driven /&gt;
    
    	&lt;bean id=&quot;entityManagerFactory&quot;
    		class=&quot;org.springframework.orm.jpa.LocalEntityManagerFactoryBean&quot;&gt;
    		&lt;property name=&quot;persistenceUnitName&quot; value=&quot;transactions-optional&quot; /&gt;
    	&lt;/bean&gt;
    
    	&lt;bean name=&quot;transactionManager&quot; class=&quot;org.springframework.orm.jpa.JpaTransactionManager&quot;&gt;
    		&lt;property name=&quot;entityManagerFactory&quot; ref=&quot;entityManagerFactory&quot; /&gt;
    	&lt;/bean&gt;
    &lt;/beans&gt;
    

    ##### test-applicationContext-dao.xml

    &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
    &lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    	xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    	xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    	&quot;&gt;
    
    	&lt;bean id=&quot;employeeDao&quot; class=&quot;com.inphina.gae.dao.EmployeeDaoImpl&quot;&gt;
    		&lt;property name=&quot;entityManagerFactory&quot; ref=&quot;entityManagerFactory&quot;&gt;&lt;/property&gt;
    	&lt;/bean&gt;
    &lt;/beans&gt;
    

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s