Integration Testing Struts 2, Spring, and Hibernate JPA

Last night with the help of loraxorg on the Spring Forums I got integration testing working under Maven 2 with Struts 2, Spring, and Hibernate JPA working. This was failing priorly due to a transient dependency in Maven 2 to an old version of Xerces. (See the Spring Forum thread for the solution.) I have not done a lot of test driven development in the past, and personally I don't really get a lot of value out of isolated Unit tests. Integration testing on the other hand I like. Real data, real connections, real functionality, and at the moment for me real failures. I'd rather know now though. One of the main issues in using JUnit for integration testing is that it instantiates a copy of your test class for each test in the class. This guarantees an isolated environment for testing, but present issues with integration testing. The main issue is that you don't want to have to reload your Spring context over and over again. In my case this is painful because Spring also triggers the initialization of my persistence context.

The Code

To overcome this I created a factory class that stores the references to my application context in a static member, and has a static method for getting a handle to the context.
package net.vitarara.quadran.core.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.ApplicationContext;

class SpringContextTestFactory {

        private static final jpaContext
        private static final datasourceContext

        public static ApplicationContext getJpaContext () {
                if (jpaContext == null) {
                        jpaContext = new ClassPathXmlApplicationContext 
                                     ("classpath*:applicationContext.xml");
                }
                return jpaContext
        }

        public static ApplicationContext getDatasourceContext () {
                if (datasourceContext == null) {
                        datasourceContext = new ClassPathXmlApplicationContext 
                                            ("classpath*:test-applicationContext.xml");
                }
                return datasourceContext
        }
}
Notice that is written in Groovy. I'm using Groovy for all of my test cases. The same principles would apply to a factory class written in Java. I have two potential application contexts, a simple one that only has a datasource, and my full one supporting my JPA environment, and all of my Struts 2 action beans, DAO's and service beans. In other words my whole application environment. Here's a sample test of a DAO:
package net.vitarara.quadran.core.data.jpa;

import net.vitarara.quadran.core.data.*;
import net.vitarara.quadran.core.test.SpringContextTestFactory;
import org.springframework.context.ApplicationContext;


class ShipmentDaoITest extends GroovyTestCase {

    void testFindForOutstandingOblReport () {
        def dao = SpringContextTestFactory.getJpaContext().getBean ("shipmentDao");
        def shipments = dao.findForOutstandingOblReport ();
        assertTrue ("Issuficient records returned", shipments.size > 20);
    }

}
I simply use my factory to get a handle to my Spring ApplicationContext, and use it to get a "shipmentDao." Then I can call any methods on it I like, testing the results. Testing a Struts 2 Action is substantially the same.
package net.vitarara.quadran.core.web.shipping

import net.vitarara.quadran.core.test.SpringContextTestFactory;

class ListShipmentsITest extends GroovyTestCase {


    void testFindByOrderNumber () {
        def action = SpringContextTestFactory.getJpaContext().getBean  
                     ("listShipmentsAction");
        def model = new ListShipmentsModel ()
        model.orderNumber = "SI142784"
        action.model = model
        action.execute ()
        assertTrue ("No shipments were found", action.shipments.size > 0)
    }
}
I use my SpringContextTestFactory to get a handle to my ApplicationContext, and get a "listShipmentsAction" bean. The listShipmentsAction beans has a dependency on a ShippingManager service bean, which has a dependency on a ShipmentDao bean, which has a dependency on a JPA EntityManagerFactory. All of these dependencies are injected by Spring, giving me an action I can test against my database. I instantiate a ListShipmentsModel, which holds my query parameters, plug in a value, set the model on the action, and call the action's execute method. Once the action has executed I can check the results any way I'd like. In this case there is a list "shipments" that is a member of the action, I check to be sure that its size is greater than 0.

Configuring Maven 2

To get all of this running you need to: Configuring Maven to compile Groovy files is quite easy. Just add the following to your <build><plugins> section.
<plugin>
    <artifactId>maven-antrun-plugin</artifactId>
        <executions>
            <execution>
                <id>compile</id>
                <phase>compile</phase>
                <configuration>
                    <tasks>
                        <taskdef name="groovyc" 
                                    classname="org.codehaus.groovy.ant.Groovyc">
                            <classpath refid="maven.compile.classpath"/>
                        </taskdef>
                        <mkdir dir="${project.build.outputDirectory}"/>
                        <groovyc destdir="${project.build.outputDirectory}" 
                                    srcdir="${basedir}/src/main/java/" 
                                    listfiles="true">
                            <classpath refid="maven.compile.classpath"/>
                        </groovyc>
                    </tasks>
                </configuration>
                <goals>
                    <goal>run</goal>
                </goals>
            </execution>
            <execution>
                <id>test-compile</id>
                <phase>test-compile</phase>
                <configuration>
                    <tasks>
                        <taskdef name="groovyc" 
                                    classname="org.codehaus.groovy.ant.Groovyc">
                            <classpath refid="maven.compile.classpath"/>
                        </taskdef>
                        <mkdir dir="${project.build.testOutputDirectory}"/>
                        <groovyc destdir="${project.build.testOutputDirectory}" 
                                     srcdir="${basedir}/src/test/java/" 
                                     listfiles="true">
                            <classpath refid="maven.test.classpath"/>
                        </groovyc>
                    </tasks>
                </configuration>
                <goals>
                    <goal>run</goal>
                </goals>
            </execution>
        </executions>
</plugin>
Now we need to create a profile that tells Maven to run our itegration tests.
<profiles>
    <profile>
        <id>itest</id>
        <activation>
            <property>
                <name>itest</name>
            </property>
        </activation>
        <build>  
            <plugins>
                <plugin> 
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>surefire-it</id>
                            <phase>integration-test</phase>
                            <goals>
                                <goal>test</goal>
                            </goals>   
                            <configuration>
                                <excludes>
                                    <exclude>none</exclude>
                                </excludes>
                                <includes>
                                    <include>**/*ITest.java</include>
                                </includes>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>
This profile attaches itself to the integration test phase and only runs test that end in ITest. (I'm not sure why the include uses **/*ITest.java, but it works.) If you already have profiles configured leave off the profiles tags. Then we need to exclude the integration tests from our unit test run. This snipped goes in your <build><plugins> section.
<plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <excludes>
            <exclude>**/*ITest.java</exclude>
        </excludes>
    </configuration>
</plugin>
The final issue you need to take care of is speficying the correct xerces dependency. Add the following to your dependencies:
<dependency>
    <groupId>xerces</groupId>
    <artifactId>xercesImpl</artifactId>
    <version>2.8.1</version>
    <scope>test</scope>
</dependency>
That tells Maven to use Xerces 2.8.1 when running tests. I place my Groovy test files in src/test/java. I don't have a separate directory for them. You could do that by configuring the Groovy compilation to look elsewhere very easily. I haven't done it because all of my tests are in Groovy, and well, I'm lazy.