Unit Testing Maven Based JPA Application on GAE

Table of contents
Reading Time: 3 minutes

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

[sourcecode language=”xml”]
<!– 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>
[/sourcecode]

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

[sourcecode language=”bash”]
#!/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
[/sourcecode]

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

[sourcecode language=”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>
[/sourcecode]

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

[sourcecode language=”java”]
public final class EMF {
private static final EntityManagerFactory emfInstance = Persistence
.createEntityManagerFactory("transactions-optional");

private EMF() {
}

public static EntityManagerFactory get() {
return emfInstance;
}
}
[/sourcecode]

And your tests would extend from LocalDatastoreTestCase

[sourcecode language=”java”]
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();
}
}
[/sourcecode]

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

[sourcecode language=”java”]
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());

}
[/sourcecode]
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

[sourcecode language=”java”]
@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();
}
}
[/sourcecode]

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

[sourcecode language=”java”]
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());
}
[/sourcecode]

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.

Written by 

Vikas is the CEO and Co-Founder of Knoldus Inc. 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). Vikas has been working in the cutting edge tech industry for 20+ years. He was an ardent fan of Java with multiple high load enterprise systems to boast of till he met Scala. His current passions include utilizing the power of Scala, Akka and Play to make Reactive and Big Data systems for niche startups and enterprises who would like to change the way software is developed. To know more, send a mail to hello@knoldus.com or visit www.knoldus.com

8 thoughts on “Unit Testing Maven Based JPA Application on GAE6 min read

  1. 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.

  2. 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

  3. 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

  4. Hi Lucio,

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

    ##### test-applicationContext.xml

    [sourcecode language=”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;
    [/sourcecode]

    ##### test-applicationContext-dao.xml
    [sourcecode language=”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;
    [/sourcecode]

Comments are closed.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading