Unit Testing Struts 2 Action Classes

Introduction

I've been reading JUnit in Action, Second Edition (http://www.manning.com/tahchiev/) which has really helped me improve my unit testing skills.  The book's author has convinced me that having automated testing as part of an application's build process is very useful both in original development and in later maintenance updates.

I now want to add unit testing to my Struts 2 web application development. This blog article discusses my attempt to unit test a Struts 2 Action class.

UPDATE: See a another article I wrote on using the Struts 2 JUnit Plugin, which I discovered after writing this article.

My first use case is testing the validation logic for a user's entries on a create account form.  The business rule is that the user name must be between 5 and 10 characters long.

Problem Unit Testing A Struts 2 Action

I cannot just write a unit test for the validate method of the Struts 2 Action class.  The validate method is called by one of the Struts 2 interceptors that fire whenever an Action class is executed.  The validate method doesn't return anything.  Rather the validate method adds an error message to fieldErrors field (which is part of the Struts 2 value stack) when a validation test fails.  The Struts 2 interceptor then checks if fieldErrors contains anything and if it does further processing is halted and the result returned will be "input".  Usually the Struts configuration is setup so that when input is returned the original JSP with the form will be displayed again along with the error messages that was added to the fieldErrors field in the validate method.

So for my unit test to work correctly, I need it to invoke the Action class within the context of the Struts 2 framework and the configuration I've setup in struts.xml.  I need the interceptors to be called so that the validate method can be properly tested. 

Unfortunately, the tutorials and information about using JUnit to test a Struts 2 Action class are pretty sparse.  The unit testing documentation for Struts 2.1.6 (http://struts.apache.org/2.1.6/docs/guides.html) points to two external links, both of which use similar code that relies on your Struts 2 application also using Spring.

I've also been reading Apache Struts 2 Web Application Development (http://www.packtpub.com/apache-struts-2-web-application-development-beginners-guide/book ).  The author provides an example in chapter 14 for unit testing a Struts 2 interceptor.  Unfortunately, the code download for the chapter's example doesn't match what's in the book, so it's hard to follow.  Additionally, the code uses several classes (XWorkTestCaseHelper,  MockActionInvocation) that are part of the xwork2 API, but the author never really explains the uses of these classes and the available documentation for the classes is minimal.  I tried to find more information about these classes and how I could use them for writing unit tests of a Struts 2 Action class, but wasn't successful.

UPDATE - I found code posted by Greg Lindholm on his blog (http://glindholm.wordpress.com/2008/06/30/unit-testing-struts-2-actions/) that provides an even cleaner way to write a unit test for a Struts 2 action class. I had to modify his code slightly (it was using a deprecated createActionProxy method). I've include this modified code in my example application (his class is StrutsTextContext and the unit test that uses his class is named TestAccountActionValidation).

So I studied the code published by Zarar Siddiqi on his blog, Depressed Programmer (http://depressedprogrammer.wordpress.com/2007/06/18/unit-testing-struts-2-actions-spring-junit/), which is one of the links the Struts 2.1.6 unit testing documentation points to.  His code was setup to work with a Struts 2 web application that also uses Spring.  I wanted something that I could use for unit testing Struts 2 without using Spring as part of the Struts 2 web application so I modified his code.

UPDATE: See the note above about the Struts 2 unit test code provided by Greg Lindholm and the reference to his blog below. Using his StrutsTestContext provides another way to write a unit test for a Struts 2 action class. The example application includes both methodologies.

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

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_2/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.

The validation logic is written in class AccountAction's validate method.  As I explained above, this method is called automatically by one of the Struts 2 interceptors that fire in response to a Struts 2 Action class being called.

Unit Testing The Validate Method's Logic

I modified the BaseStrutsTestCase class published on the Depressed Programmer blog (see reference below).  I stripped out the code that looked for the Spring configuration file since my application isn't using Spring.  I also removed some other code that was setting up request and response objects that weren't being used and added code that sets up request parameters that will be provided to the Action class.  The original BaseStrutsTestCase also used a deprecated method from the xwork2 ActionProxyFactory class so I replaced that method call with the new method.

The new version of BaseStrutsTestCase is in the src/test/java folder.  To write my unit test for the logic in the Struts 2 Action class's validate method I created a class that extends the BaseStrutsTestCase (see class TestAccountAction).  This class inherits the two methods defined in BaseStrutsTestCase (createAction and setup). 

UPDATE: See the note above about the Struts 2 unit test code provided by Greg Lindholm and the reference to his blog below. Using his StrutsTestContext provides another way to write a unit test for a Struts 2 action class. The example application includes both methodologies.

Inside each test method I do something similar to the following:


createAction(AccountAction.class, "/site",
"createaccount", "execute"); 

Map<String, Object> params = new HashMap<String,
Object>();

params.put("accountBean.userName",
"Bruc");

params.put("accountBean.password",
"test");

proxy.getInvocation().getInvocationContext().setParameters(params);

String result = proxy.execute(); 

assertEquals("Result does not equal input",
"input", result);  

The call to createAction is to the method inherited from BaseStrutsTestCase.  That method does the work of setting up a proxy Action class (based on the Action class I pass to this method) to run within the Struts 2 framework and my struts.xml configuration so that all the interceptors are fired. 

I then create a HashMap that will contain the request parameters to model what a user has entered into the form fields.  The key is the form field name and the value is the value I want that request parameter to have.  For each test I vary the values to test different scenarios (e.g. user name too long, too short, just right).  I then place this HashMap as part of the context that will be used when the proxy is executed.

I then call proxy.execute() which will run my Action class as part of the Struts 2 framework with the configuration I've setup in struts.xml.  The String result returned (e.g. "success" or "input") is stored in the result variable.  I then use the JUnit assertEquals method to determine if the result is what it should be.  In this case the validation should catch that the user name is too short and the result value should be input (meaning the execute method was never called, further processing was halted).

You can also test for the presence of keys and values in the fieldErrors Map.  See method testUserNameErrorMessage for an example.

Summary

Using the work done by Zarar Siddiqi, Greg Lindholm, and others I was able to create a unit test for testing my Struts 2 Action class's validation logic.  I can add this test to my automated build process so that it will run each time.  So I've completed the first step in unit testing my Struts 2 Action class.

The benefits of automating the tests of the Struts 2 Action class are several. I can rerun the tests anytime the code changes and each time the project is built to help ensure a future change doesn't break something. Even after writing the unit tests, I saved time compared to manually testing the Action class by running the project inside a servlet container (e.g. Tomcat) and manually entering different user names. I can easily add additional tests should the business rules change (for example if the password value must be a certain length or a specific format).

Future Research

I need to further experiment using the BaseStrutsTestCase to write unit tests for validation that occurs in a separate validation xml file (Struts 2's validation framework).  I also need to research further the methodology for unit testing custom interceptors (as demonstrated in chapter 14 of Apache Struts 2 Web Application Development). 

If you have any suggestions for improving the above or even better if you have your own examples of unit testing Struts 2 Action classes please post a comment.  Good examples of unit testing Struts 2 action classes are hard to find!

References

JUnit in Action, Second Edition, http://www.manning.com/tahchiev/

Struts 2.1.6 Documentation, http://struts.apache.org/2.1.6/docs/guides.html

Unit Testing Struts 2 Actions wired with Spring using JUnit, http://depressedprogrammer.wordpress.com/2007/06/18/unit-testing-struts-2-actions-spring-junit/

Unit Testing Struts 2.0 (Part 3), http://fassisrosa.blogspot.com/2007/09/unit-testing-struts-20-part-3.html

Glindholm's Weblog, http://glindholm.wordpress.com/2008/06/30/unit-testing-struts-2-actions/

Apache Struts 2 Web Application Development, Dave Newton, June 2009, Packt Publishing, http://www.packtpub.com/apache-struts-2-web-application-development-beginners-guide/book

Apache Maven Project

Maven: The Definitive Guide, http://www.sonatype.com/books/maven-book/reference/public-book.html

Jetty, http://jetty.mortbay.org/jetty5/index.html

Related Blog Entries

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Hi . Thank you for this article.
But i have some problem when i run in mvn test in command line it some error occures
Tests in error:
testUserNameLessThenMinimumLength<edu.ku.it.si.struts2_junit_exmple.action.TestAccountAction>: Unable load configuration.

Can you help me please.

Thank you.
# Posted By Lusi | 5/30/11 2:11 AM
Lusi - I downloaded the example application, unzipped, and was able to run mvn test successfully. So I cannot duplicate the problem you had.

You may also want to read:

/blog/index.cfm/2009/...

Which has more information on unit testing Struts 2 Action classes.
# Posted By Bruce | 5/30/11 7:23 AM
You have no idea how long I have been looking for "proxy.getInvocation().getInvocationContext().setParameters(params);"

You saved my life. I can now updated all my UnitTest to pass by the Request rather than directly poking the data into the action!!

Great Stuff!!
# Posted By Garry Taylor | 7/21/11 6:35 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1.002. Contact Blog Owner