Using JUnit To Test A Struts 2 Action Class In An Application That Also Uses Spring
Introduction
In my Struts 2 web applications, I'm now using Spring to manage class relationships. Creating unit tests for a Struts 2 ActionSupport class in an application that also uses Spring can be difficult. Fortunately, there is a Struts 2 plugin available that makes it easier to write unit tests for ActionSupport class.
Struts 2 JUnit Plugin
Struts 2 provides the Struts 2 JUnit Plugin Jar that you can include on your application's class path so your test classes can use it. You can get the plugin by using this dependency in your pom.xml.
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-junit-plugin</artifactId>
<version>2.1.8</version>
</dependency>
The Jar is also available in the complete Struts 2 download (http://struts.apache.org). The Struts 2 JUnit Plugin Jar can be found in the lib folder after unzipping the download.
Struts 2 and Spring Together - Unit Testing
When using Struts 2 and Spring together, the service objects used by the Struts ActionSupport class are managed by the Spring container (bean factory) and injected into the ActionSupport class. For example an application with a SurveyAction class (extends ActionSupport) that has this property:
will use Spring to inject an object of type SurveyService into the SurveyAction class.
When I create a test for the SurveyAction class, I want the test to execute within the Struts 2 and Spring frameworks. Given the above property (surveyService) my test needs the Spring bean factory to provide a SurveyService object to the SurveyAction class prior to the test method being executed.
I've previously written about the Struts 2 JUnit Plugin, but that article's example application did not use Spring. However, the Struts 2 JUnit Plugin provides two classes (StrutsTestCase and StrutsSpringTestCase). Your test class should extend StrutsSpringTestCase if your application is also using Spring.
Extending the StrutsSpringTestCase class will cause the Spring framework to also be run prior to your test method executing. Therefore any objects your application needs injected into the ActionSupport class by Spring will get done (provided you defined those beans in your Spring configuration file).
By default the StrutsSpringTestCase looks for the Spring configuration file directly in the class path root and expects the configuration file to be named applicationContext.xml. If you need to have a different location or name for your Spring configuration file then in your test class override method getContextLocations.
For example:
@Override
public String getContextLocations() {
return "edu/ku/it/si/lovekusurvey/action/TestSurveyAction-context.xml";
}
NOTE: Beginning in Struts 2 versions 2.3 and on the getContextLocations method should return a Array of Strings. Each element of the array is a complete path to the Spring configuration XML file.
Example Application
You can download a complete example application that demonstrates testing a Struts 2 ActionSupport class in an application that uses Spring. The example application was created using Eclipse 3.5 with the Maven 2 plugin. You should be able to import the archive download directly into Eclipse.
If you are not using Eclipse, unzip the download and if your Java IDE supports importing Maven projects import the unzipped project.
You can run the application using the Maven command mvn jetty:run in a command (terminal) window after navigating to the project's root folder (the location of Maven's pom.xml file). When you see "[INFO] Started Jetty Server" in the command window open a web browser and load this URL: http://localhost:8080/LoveKUSurvey/index.html.
To stop the Jetty server type CTRL-C in the command window.
To run all the tests type the Maven command mvn test in the command window (again be in the project's root folder). The test class that is used to test the SurveyAction ActionSupport class is located in src/test/java - package edu.ku.it.si.lovekusurvey.action and is named TestSurveyAction. Below is the testExecute method from that class.
public void testExecute() throws Exception {
request.setParameter("answer", "3");
ActionProxy proxy = getActionProxy("/saveAnswer");
SurveyAction action = (SurveyAction) proxy.getAction();
assertNotNull(action);
String result = proxy.execute();
assertEquals("success not returned from calling action class", Action.SUCCESS, result);
assertEquals("3 not value of answer", 3, action.getAnswer() );
log.info("Yes percentage is " + action.getYesPercentage() );
log.info("No percentage is " + action.getNoPercentage() );
log.info("Not sure percentage is " + action.getNotSurePercentage() );
assertEquals("yesPercentage not equal to 70, but it should be", 70, action.getYesPercentage() );
assertEquals("noPercentage not equal to 20, but it should be", 20, action.getNoPercentage() );
assertEquals("notSurePercentage not equal to 10, but it should be", 10, action.getNotSurePercentage() );
}
The first step in this method is to set the value of the request parameter named answer to simulate the user having selected the radio button option that has a value of 3 (the Yes radio button) on the survey form. Then using the name of the action (saveAnswer) as defined in the struts.xml configuration file I get an ActionProxy object that I can use to get a reference to the ActionSupport class that is actually being called for the saveAnswer Struts action.
I can then tell the proxy to execute the action. The ActionSupport class SurveyAction will have been injected with an object of type SurveyService to use for this test. If you examine the test Spring configuration file (located in src/test/java - package edu.ku.it.si.lovekusurvey.action and named TestSurveyAction-context.xml) you'll see the surveyService bean and its configuration that Spring injected.
Once the execute method of the SurveyAction class has run, I can then test for a specific result being returned ("success" is the expected result value for this test). I can also test the value of the various instance fields of the SurveyAction class to ensure they are correct given value of the initial request parameter answer.
Summary
Using the Struts 2 JUnit Plugin makes it much easier to test my Struts ActionSupport classes even when my application is also using Spring. Consult the references below and study my example application to learn how to use the Struts 2 JUnit Plugin. Please comment if you find anything that should be changed or if you have additional information about using the Struts 2 JUnit Plugin.
References
- Example Application
- Struts 2 JUnit Plugin, version 2.1.8, API
- Unit Testing A Struts 2 Action Class - Struts 2 JUnit Plugin, Bruce Phillips Blog, September 2009
- How can I test my action output, validation or the action execution outside a container?, Struts 2 Documentation FAQ, http://cwiki.apache.org/confluence/pages/viewpage.action?pageId=2853342
- How Do You Test Your Action Classes, Struts 2 User Mailing
List, http://www.nabble.com/how-do-you-test-your-action-classes--to25289507.html
- Struts 2 JUnit Plugin Documentation, http://cwiki.apache.org/confluence/display/WW/JUnit+Plugin
http://www.digitalsanctum.com/2010/01/25/how-to-te...
My approach allows you to run Struts test cases transactionally which means any database operations are automatically rolled back.
http://old.nabble.com/how-do-you-test-your-action-...
java.io.FileNotFoundException: class path resource [WEB-INF/content/] cannot be resolved to URL because it does not exist
struts.xml does reside at the folder . and hence not able to load the action class ...any thoughts ?
Bruce
2011-10-13 10:49:02,884 WARN org.springframework.mock.web.MockServletContext.getResourcePaths:221 - Couldn't get resource paths for class path resource [WEB-INF/content/]
java.io.FileNotFoundException: class path resource [WEB-INF/content/] cannot be resolved to URL because it does not exist
Can you brief the structure of the files that need to be placed in the application.
I want to use applicationContext.xml where should I place this file.
Please mention the jars that are required for doing this.
If you explain step by step one more time,It would be fine...
Thank you
If you download the example application (see link in article) and unzip it you can examine how the project is organized.
The project uses Maven to manage dependent artifacts so if you read the pom.xml you'll see the jars required.