Vita Rara: A Life Uncommon

Integration Testing Struts 2, Spring, and Hibernate JPA


Categories: | | | |

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:

  • Configure Maven to use Groovy and compile your Groovy test classes.
  • Configure Maven to run your integration tests.
  • Exclude your integration tests from normal test runs.
  • Fix the Xerces dependency.

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.

Integration between Struts 2 + Spring 2+ Hibernate +AJAX

hi,

I have some configuration problems about using Struts 2 + Spring + Hibernate...

I would like to know how it's possible to integrate these technologies.. I've followed the example proposed in the Struts 2 article at :

http://struts.apache.org/2.0.8/docs/struts-2-spring-2-jpa-ajax.html

But i wasn't able to make it work. I have included all the libraries except the struts2-api.jar, because i was not able to find it. Probably, i haven't included the right libraries, but i don't know...
Can you help me?

Thank you a lot.
Luca

download hibernate lib

download hibernate lib.then try onces again

Dharma

Change web.xml file

You need to add an extra filter tag in web.xml file. In that same site there is a link on top called view change. Just click on it. You can find the new entry that you need to add in web.xml file. Hope that helps.

Struts 2 + Spring 2 + JPA + AJAX

The added tag you refer to is already in the example. It still does not work for me.
Can someone list all the JAR files and the proper version that are needed to run this application? From reading of people's issues I see that im many cases JAR files are either not up to date or conflict in version. In my case I try to develop an application that uses Struts2, Hibernate, Tiles.

Thanks for any input
Elan

Jar Files, etc.

To be honest I haven't managed any of the jar files in my project. I just started with the Struts 2 Maven archetype and added dependencies from there. It has worked fine for us so far, which is over 18 months of development.

add filter entry in web.xml

In web.xml file, add the below entry :-

Spring OpenEntityManagerInViewFilter

org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter

Spring OpenEntityManagerInViewFilter
/*

Re: Integration between Struts 2 + Spring 2+ Hibernate +AJAX

I have a project using Struts 2 + Spring 2 + JPA (backed by Hibernate). I've recently started using Prototype to add Ajax functionality to it. I use Maven to manage the project. If you send me an email. (Contact info is on the home page.) I'll send you a copy of my pom.xml file to take a look at.

Mark

Need a sample project

Hi Mark,
I need a sample project on Integration of Spring 2+ Struts 2+ hibernate and Ajax.

If you have the code could you please share with me.
My id is karthik_talasu@yahoo.com
talasu.kartik@in.fujitsu.com

Regards,
Kartik

Very good

I had this maven application running in Jetty server, but frequently getting OutOfMemoryError when the server re publishes many times.

a question

Do you know if it is possible to use Spring's OpenEntityManagerInViewInterceptor with Struts 2? I tried to configure it in spring-context.xml as a spring-managed bean and reference it in struts.xml but it looks like it does not work. How can you solve the lazy loading issue when you use JPA and struts 2? Thank you. -Li

I tried to get this working

I tried to get this working but failed. The option that i chose was to use the OpenEntityManagerInViewFilter instead of the interceptor.

Here's the snippet of my web.xml where i configured it. Be careful to declare the jpa filter mapping before the struts filter mapping.

   <filter>
      <filter-name>jpaFilter</filter-name>
      <filter-class>
         org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
      </filter-class>
   </filter>
   <filter-mapping>
      <filter-name>jpaFilter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>
   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>

Hi Could you please guide

Hi
Could you please guide me
I am facing a problem in struts 2 It is just to dispay hello world
if I inculde this line in web.xml file
org.apache.struts2.dispatcher.FilterDispatcher
I am not able to ren the application if I include in web.xml
Please guide me how to do

Use Maven

I have never bootstrapped a Struts 2 application on my own. I started with the Maven Struts 2 archetype and went from there. That's what I would recommend.

Mark

JPA Open Session in View

Hi Li,

I haven't used the OpenEntityManagerInViewInterceptor yet. In general I have shied away from using the open session in view methodology in my development.

With that said I did accidentally create an open entity manager in the view. I used the EntityManagerFactory to make an EntityManager in a DAO. This new em was not tied to the transaction, and stayed open all the way through the view layer. It really confused me. That was when I figured out how to get a transactional em in my DAO's, Getting a Transactional EntityManager in a JpaDaoSupport Derived DAO.