ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Unit Test Your Struts Application
Pages: 1, 2, 3, 4, 5

StrutsUT AspectJ Solution

Compared to the traditional solution, the AspectJ solution is more transparent to the test case writer. There's no extension to the Struts central controller and test case base class, just an added aspect: StrutsTestCaseAspect.

Refer to the References section at the end of the article to learn more about AspectJ.

// StrutsTestCaseAspect.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.cactus.ServletTestCase;
import org.apache.struts.action.Action;
import org.jingle.unittest.struts.util.*;
import org.jingle.unittest.struts.*;

public aspect StrutsTestCaseAspect {
    
  pointcut setup(ServletTestCase testcase) 
  : execution(protected void org.apache.cactus.ServletTestCase+.setUp())
    && this(testcase);
    
  pointcut teardown(ServletTestCase testcase) 
  : execution(protected void org.apache.cactus.ServletTestCase+.tearDown())
    && this(testcase);
    
  pointcut actionExecute(ActionMapping mapping, ActionForm form, 
           HttpServletRequest request, HttpServletResponse response) 
  : execution(public ActionForward org.apache.struts.action.Action+.execute
       (ActionMapping, ActionForm, HttpServletRequest, HttpServletResponse))
    && args(mapping, form, request, response);
    
  after(ServletTestCase testcase) 
  : setup(testcase) 
    && (!cflowbelow(setup(ServletTestCase))) 
    && (!within(org.jingle.unittest.struts.StrutsServletTestCase+)) {
       ... //add extra info about the test point and test case instance
  }
    
  before(ServletTestCase testcase)
  : teardown(testcase) 
    && (!cflowbelow(teardown(ServletTestCase))) 
    && (!within(org.jingle.unittest.struts.StrutsServletTestCase+)) {
       ... //clear the extra info
  }
    
  ActionForward around(ActionMapping mapping, 
                       ActionForm form, 
                       HttpServletRequest request, 
                       HttpServletResponse response) throws Exception
  : actionExecute(mapping, form, request, response) 
    && (!cflowbelow(actionExecute(ActionMapping, 
                                  ActionForm, 
                                  HttpServletRequest, 
                                  HttpServletResponse))) 
    && if (request.getAttribute(
         StrutsTestCaseConstants.WRAPPER_ACTION_ATTR_NAME) == null) {
      ... //introduce the join points before and after the execution()
          //in Action
  }
}

There are three pointcuts defined in the aspect: setup, teardown, and actionExecute.

  • The setup pointcut will be captured after the setUp() method in the test case executes, to save extra information about the test point and test case instance.

  • The teardown pointcut will be captured before tearDown()'s method execution in the test case, to clear any extra information.

  • The actionExecute pointcut will be captured during the execute() method's execution in the Action class to introduce join points.

A Simple Test Case

The AspectsimpleStrutsTest is almost the same as SimpleStrutsTest used in the traditional solution, except that it extends the ServletTestCase defined in the Cactus framework, instead of StrutsServletTestCase (provided by StrutsUT).

// AspectSimpleStrutsTest.java
package unittest.struts;

...
public class AspectSimpleStrutsTest extends ServletTestCase {
    ... //same as SimpleStrutsTest
}

How to Write the Test Case

Writing test cases in the AspectJ solution is similar to how you write them in the traditional solution, except for the first rule: each test case should extend from the ServletTestCase defined in Cactus framework.

The more complicated test case in the AspectJ solution, AspectAnotherStrutsTest, is also available in the sample code package.

How to Install StrutsUT

Here are the steps to install StrutsUT:

  • Prepare the web container. It can be Tomcat, JBoss, or another web container product.

  • Install the Struts framework into your web container.

  • Install Cactus into your web container.

  • Copy StrutsUT.jar into your web application lib directory: %CONTEXT_PATH%/WEB-INF/lib.

  • If you're using mocks, copy easymock.jar into the web application lib directory.

  • If you're using the StrutsUT traditional solution, modify the Struts configuration file (it is most likely named struts-config.xml) in %CONTEXT_PATH%/WEB-INF. Search for the following lines in the file:

    <controller processorClass="org.apache.struts.action.RequestProcessor"
    </controller>

    And replace that with:

    <controller processorClass="org.jingle.unittest.struts.StrutsUnitTestRequestProcessor"
    </controller>

    If you're using the Tiles plug-in with Struts, replace the following lines:

    <controller
        processorClass="org.apache.struts.tiles.TilesRequestProcessor"
    </controller>

    with:

    <controller
        processorClass="org.jingle.unittest.struts.TilesUnitTestRequestProcessor"
    </controller>
  • If you're using the StrutsUT AspectJ solution, extract StrutsTestCaseAspect.java from the StrutsUT .jar file and compile with your source code and test cases using the AspectJ compiler.

Now, the test case is ready to be launched by the JUnit test runner.

Conclusion

Struts is a widely used framework for web applications. Cactus is a web application unit-test framework. They are two sharp weapons for web application developers, but there are some limitations when unit testing Struts applications using Cactus. StrutsUT provides two solutions to solve this problem by introducing a join point into the Struts execution flow. The traditional solution extends the Struts Request Processor and Cactus test-case base class. The AspectJ solution keeps the Cactus and Struts framework untouched but acquires the source code and test cases compiled with the AspectJ compiler. In the future, this limitation will be removed if the AspectJ compiler supports runtime weaving.

References

Lu Jian is a senior Java architect/developer with four years of Java development experience.


Return to ONJava.com.