Unit Testing A Struts 2 Action Class - Struts 2 JUnit Plugin
Introduction
In my previous article on unit testing Struts 2 Action classes I discussed and demonstrated how to write unit tests that used classes provided by some other developers. After researching unit testing Struts 2 further, I found out that the 2.1.8 release of Struts 2 will include a new version of the JUnit plugin that will make it even easier to write unit tests for your Action classes.
You don't have to wait for the next release to take
advantage of the code written for the improved JUnit plugin. First you'll want to read
the information about the plugin (see: http://cwiki.apache.org/confluence/pages/viewpage.action?pageId=2853342). (NOTE: Struts 2.1.8 is now available and you can download the Struts 2.1.8 JUnit Plugin as part of the Struts 2 download on http://struts.apache.org. The plugin is also available in the Maven repository.)
Second you can check out the code used for the plugin from the Struts 2
Subversion repository (http://svn.apache.org/repos/asf/struts/struts2/trunk/plugins/junit).
In this blog article I'll discuss how I used the class StrutsTestCase (one of the classes you'll get when you check out the code for the JUnit plugin) to write unit tests for a Struts 2 Action class.
Example Application
You can download an example application that includes a unit test (using JUnit) for a Struts 2 Action class. The example application is a Maven project (created using Eclipse 3.5 with the Maven 2 plugin) so you'll need Maven (http://maven.apache.org/) installed to use it. After unzipping the download you should be able to import the project into your Java IDE (if your IDE supports Maven).
You can run the unit tests by opening a command window and navigating to where you unzipped the example. You'll need to be in the same directory as the project's pom.xml file. Once in that directory type mvn test and Maven will run the tests.
The example is a simple account creation program. You can run the example itself by typing mvn jetty:run in the command window. When you see [INFO] Started Jetty Server open a web browser and navigate to this URL: http://localhost:8080/struts2_junit_example_3/createaccount.jsp. If you fill in the user name field with a value of less then 5 or more than 10 characters the page should reload and display an appropriate error message. Type Control C in the command window to stop the Jetty server.
My goal in this unit test is to write a test for my validation logic. The validation logic is written in class AccountAction's validate method. In this example the business rule is that a user name must be between 5 and 10 characters. The validate method is called automatically by one of the Struts 2 interceptors that fire in response to a Struts 2 Action class being called. That is why we need our test to run within the context of the Struts 2 framework and our Struts configuration. The Struts 2 JUnit plugin makes it easy to write units tests for an Action class, where the action will be executed within the Struts 2 framework and our configuration.
Using the JUnit Plugin – Sort Of
Since the new version of the JUnit Plugin for Struts 2 hasn't yet been released
yet, you'll need to checkout the plugin's latest source code as described above. (See note above, 2.1.8 is now released.)
There are two key classes in the source code StrutsTestCase and
StrutsSpringTestCase (used when your Struts 2 application also uses Spring). I
copied the StrutsTestCase class into the test folder of the Struts 2 application I'm testing.
To write a unit test, I created a Test class that extends StrutsTestCase (see class TestAccounActionUsingStrutsTestCase). Then in each test method I do something similar to the following:
request.setParameter("accountBean.userName",
"Bruc");
request.setParameter("accountBean.password",
"test");
ActionProxy proxy =
getActionProxy("/createaccount");
String result = proxy.execute();
assertEquals("input not returned from calling action
class", Action.INPUT, result);
First I set the request parameters needed by the Action class and referenced in the validate method. I then pass the action string to method getActionProxy, which returns a com.opensymphony.xwork2.ActionProxy object. When I call the execute method on the ActionProxy object, my Struts 2 Action class will be executed within the context of the Struts 2 framework and the Struts configuration for the action.
The result returned by executing the action is stored in String result. I can then use the JUnit assertEquals method to compare the result to what I expected. In this unit test, the result should be "input" since the validation logic in the AccountAction validate method should catch that the user name is too short.
Using the StrutsTestCase class you can also write unit test for checking many other aspects about your Action class. See methods testGetActionMapping, testGetActionProxy, and testUserNameErrorMessage in my example's test class.
The notes on the Struts 2 wiki indicate that you can even use the StrutsTestCase class to write tests for testing the output of your Action class (the view rendered). Though if your output is a JSP, the notes indicate you also need to use an additional plugin.
Summary
Using the StrutsTestCase class makes it easy to write unit tests of a Struts 2 Action class. It will be even better once the new version of the Struts 2 JUnit plugin is released and you can just reference it in your pom.xml in order to be able to use the StrutsTestCase class. Again, please consult the Struts 2 documentation (see reference below) for more details on the JUnit plugin and using the StrutsTestCase class.
See this article for an example of using the Struts 2 JUnit Plugin when your Struts 2 application also uses Spring.
References
- 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
Map session = ActionContext.getContext().getSession();
session.put("username", username);
I have 2 problems then :
1) getSession returns null. How can I set this one in the Junit-test itself
2) I want to check in the Junit-test what is the content of the session-object. How can I get to it??
In the unit test you must set the session map object and its keys/values. For example:
HashMap<String, Object> sessionMap = new HashMap<String, Object>();
sessionMap.put("displayName", "Kit Cole");
sessionMap.put("isAuthorized", new Boolean("true") );
List<String> userAllowedGroups = new ArrayList<String>();
userAllowedGroups.add("ku:is:it:idms:fulladmin");
sessionMap.put("userAllowedGroups", userAllowedGroups);
request.addHeader("uid", "kitcole");
request.setParameter("onlineId", "bphillips");
ActionProxy proxy = getActionProxy("inputLockAccount");
proxy.getInvocation().getInvocationContext().setSession(sessionMap);
Since my Action class implements SessionAware, it has a setSession method and an instance field named session of type Map<String, Object>.
After you execute the action you can call the getSession method to get the session object:
LockAccountAction action = (LockAccountAction) proxy.getAction() ;
assertNotNull(action);
String result = proxy.execute() ;
sessionMap = action.getSession() ;
proxy.getInvocation().getInvocationContext().setSession(sessionMap);
If you're running the entire interceptor stack - and if you're using a bean with scope session - I found out that you need this line in addition to action.setSession(session). And you also need:
http://blog.solidcraft.eu/2011/04/how-to-test-spri...