| By Kishore Kumar | Article Rating: |
|
| August 5, 2004 12:00 AM EDT | Reads: |
18,240 |
Apache Cactus is part of the Jakarta project and is an open source framework for unit testing server-side Java code. It uses and extends the JUnit framework and facilitates unit testing of servlets, JSPs, Taglibs, EJBs, and filters.
Testing server-side components is more complicated than testing client-side code because these components interact with a container and require access to many container-managed objects such as request and session. It's possible to make a mock-up of all the container-managed objects and test the components. These mock objects provide a "clean" environment for testing that is totally isolated from the container. The other approach is to use an in-container strategy. Using this, the test code runs on a real (not mock) container and uses real container-managed objects. Both approaches have their advantages and disadvantages. Cactus is based on the in-container testing and our discussion will focus on this approach.
I'll explore how to use the Cactus framework to write JUnit-based test classes for testing server-side components.
Understanding Cactus - How It Works
Cactus tests are organized into Cactus TestCase classes. You can subclass and implement any of the three provided Cactus TestCase classes: ServletTestCase, JspTestCase, and FilterTestCase. Figure 1 explains the overall system.Here, XXX is the name of the test. Unlike a JUnit test, the Cactus test runs in two different environments: client-side and server-side. The class-under-test is a server component like a servlet or a JSP. The following are the different steps that occur when a Cactus test (testXXX) is run:
- The JUnit TestRunner executes the TestCase method runTest. If defined, the method beginXXX is executed. This method may be implemented to initialize a Web request (HTTP parameters and headers) to the server-side component-under-test.
- Cactus opens an HTTP connection to a Cactus redirector proxy. All the parameters set up in step 1 are sent in the HTTP request.
- The redirector acts like a proxy on the server-side for your tests. It creates a new instance of the TestCase class and executes the test. The TestCase class is instantiated twice: once on the client-side (by the TestRunner) and once on the server-side (by the redirector proxy). The client-side instance is used to run the method beginXXX and endXXX and the server-side instance is used to run the test methods. The redirector also initializes the TestCase instance with server-side implicit objects (HttpServletRequest, HttpServletResponse, ServletContext,...) which are made available to the test methods.

- The setUp method is executed. If required, implement this method to define a test fixture.
- The redirector executes the test method (testXXX).
- The test method usually instantiates the component-under-test and invokes the methods that need to be tested. It uses JUnit assert API (assertEquals, assert,...) to verify the result.
- The tearDown method is executed. If required, implement this method to do clean up.
- If the test fails, the redirector proxy handles the exception thrown from testXXX.
- If an exception has been raised, the proxy returns the exception information back to the client side.
- If no exception has occurred, the method endXXX(org.apache.cactus.WebResponse) or endXXX(com.meterware.httpunit.WebResponse)* is executed if it is defined. This method may be implemented to verify the response from the server-side component.
Cactus Redirectors
Cactus provides three redirectors: ServletRedirector, JspRedirector, and FilterRedirector. Cactus TestCase uses a corresponding redirector implementation (for example, ServletTestCase uses ServletRedirector). The implicit objects that are created and initialized in a Cactus test instance depend on the specific redirector. A servlet redirector initializes a servlet test case with servlet API objects. On the other hand, a JSP redirector initializes a JSP test case instance with JSP API objects.
Writing a Cactus Test
To write a Cactus test, complete the following steps:1. Implement a subclass of a Cactus TestCase implementation. Subclass the ServletTestCase class if your component-under-test is a servlet or subclass the JspTestCase class if your component-under-test uses JSP API objects. If your component-under-test is a servlet filter, extend your test from FilterTestCase.
public class TestSampleServlet extends ServletTestCase
{
}
2. Implement standard JUnit methods. As in a normal JUnit test, define the following JUnit methods in your test case class:
- A constructor with a single string parameter, which is the test name that needs to be executed when the test is run.
- A method suite to collect the test into a JUnit TestSuite object. Running the TestSuite will run all contained tests. A convenient TestSuite constructor can create a suite object that contains test case instances for every method starting with "test" in a given class.
public TestSampleServlet(String testName) { super(testName); } public static Test suite() { return new TestSuite(TestSampleServlet.class); } - Override the method setUp to initialize a test fixture and the method tearDown to clean up the fixture. These are executed at the server side and all the server-side implicit objects are available to these methods.
- Instantiate the component-under-test. Since the test case extends Cactus TestCase, server-side implicit objects are defined and initialized with valid values. These are available to the test methods through TestCase instance members.
- Call the method to be tested.
- Perform JUnit standard asserts (assertTrue, assert, ...) to verify the result.
public void testXXX()
{
SampleServlet servlet=new SampleServlet();
// session is an implicit object defined in cactus TestCase class
session.setAttribute("name","value");
String result=servlet.doSomething(request);
assertEquals("some value",result);
}
4. Implement the method beginXXX to initialize the HTTP request to the server. 5. Implement the method endXXX to verify the HTTP response from the server.
Testing Servlets
You need to subclass and implement a ServletTestCase to test a servlet. The ServletTestCase provides the following implicit objects: request (HttpServletRequest), response (HttpServletResponse), session (HttpSession), and config (ServletConfig). The ServletTestCase uses the ServletRedirector as the proxy to servlet tests.
public void beginXXX(WebRequest request)
{
request.addParameter("param1","value");
}
public void testXXX()
{
ServletToTest s=new ServletToTest();
s.init(config);
s.methodToTest();
assertEquals("some value", session.getAttribute("result"));
}
public void endXXX(WebResponse response)
{
Cookie cookie=response.getCookie("someCookie");
assertEquals("some value",cookie.getvalue());
}
Testing JSPs
Testing JSPs covers the following: verifying the result of JSP processing (HTML) and unit testing JSP tag libraries. Unit testing tag libraries are discussed later.
You can still have your test case class extend from ServletTestCase if your test does not use any of the JSP API objects (like PageContext).
The Web response can be easily verified by implementing the method endXXX in a ServletTestCase subclass.
public class SimpleTest extends ServletTestCase
{
[...]
public void testXXX()
{
RequestDispatcher rd=config.getServletContext().
getRequestDispatcher("test.jsp").
rd.forward(request,response);
}
}
public void endXXX(org.apache.cactus.WebResponse webResponse)
{
// Assert Result
[ ... ]
}
Cactus also integrates HttpUnit into the framework. The HttpUnit implementation of the WebResponse object (com.meterware.httpunit.WebResponse theResponse) can be used to verify the HTML response.
public void endXXX(com.meterware.httpunit.WebResponse theResponse)
{
WebTable table = theResponse.getTables()[0];
assertEquals("rows", 4, table.getRowCount());
assertEquals("columns", 3, table.getColumnCount());
assertEquals("links", 1, table.getTableCell(0, 2).getLinks().length);
}
Testing JSP Tag Libraries
For testing JSP Tag libraries, extend your test case class from JspTestCase. In addition to the servlet implicit objects, JspTestCase provides the following implicit objects: out (JspWriter) and pageContext (PageContext). These implicit objects are made available to the setUp, tearDown, and textXXX methods as instance variables of the JspTestCase class.To test the tag handler, use the implicit objects provided by the JspTestCase to set up initial state for the test. Then create and initialize your custom tag using the pageContext implicit object. After setting up the tag, call the tag life-cycle methods in the correct order and verify the results. The tag's output can be inspected in the endXXX method.
Complete the following to set up the custom tag for testing:
1. Create the custom tag and initialize it with the pageContext implicit object:
MyTag tag=new MyTag(); // pageContext is available as an implicit object to JspTestCase tag.setPageContext(pageContext);
2. Set the tag's attributes:
tag.setNum1("10");
tag.setNum2("11");
3. Set the parent tag (optional):
Tag.setParent(enclosingTag);
The "enclosingTag" will have to be instantiated and set up as well. This will allow the tag to successfully call the method getParent.
4. Create the BodyContent object (optional): If the tag processes its body, call pageContext.pushBody() to obtain a BodyContent and the corresponding pageContext.popBody() after the tag completes execution.
5. Set up page state (optional): Set up appropriate objects into the request or pageContext for use by the tag.
Once the tag has been set up, test the tag by calling its relevant life-cycle methods and using JUnit assert API to verify the results.
Verifying Individual Methods
You can verify a tag that conditionally includes its body based on some values:
tag.setValueThatResultsInIncludingBodyContent("Correct Value");
assertEquals(Tag.EVAL_BODY_INCLUDE,tag.doStartTag());
Verifying Tag Output
The custom tag output can easily be verified in the endXXX method of the test case.
Testing Iteration Tags
You can test a tag that repeats its body output a number of times as shown:
// [...] set up tag state
int count=0;
do
{
count++;
} while (tag.doAfterBody() == Tag.EVAL_BODY_AGAIN);
assetEquals(EXPECTED_RESULT,count);
Testing Body Tags
For testing tags with body content you must replicate the life cycle of the tag in your test code. Use the page context implicit object to obtain and release a BodyContent object:
tag.setPageContext(pageContext);
tag.doStartTag();
// if the doStartTag method return EVAL_BODY_TAG
BodyContent bodyContent=pageContext.pushBody();
tag.setBodyContent(bodyContent);
tag.doInitBody();
bodyContent.println("Sample content");
tag.doAfterBody();
tag.doEndTag();
pageContext.popBody();
Implement the endXXX method to verify whether the tag returns the expected body content or not.
Testing Filter
Your test case class should extend from FilterTestCase when you want to test servlet filters. Cactus automatically provides the implicit object filterChain (FilterChain), in addition to all the servlet implicit objects, to the setUp, testXXX, and tearDown methods. In your test method, do the following:
- Instantiate the Filter class.
- Set up the required request parameters.
- To simulate the next filter in the filter chain, define a mock filter chain inner class.
- Invoke the doFilter method. Pass the mock filter chain object as a parameter to doFilter if you need to verify if the filter is returning to the correct filter in the chain.
EJBs can be tested from any of the Cactus redirectors. From the testXXX method, obtain the home reference to your EJB, create an instance of it, invoke the method to test, and finally assert the result.
Running Cactus Tests
Cactus provides a ServletTestRunner to run the Cactus tests using a browser. In addition to the Cactus redirectors, you'll also need to map this servlet in the web.xml file of your Web application. Once the Web application is deployed, you can run your application using the URL http://server:port/webapp/ServletTestRunner?suite=SimpleTestServlet. This assumes that the class SimpleServletTest has a static method suite that will provide the test runner with a TestSuite that it can run. The test runner will return the test results as XML data to the browser.Summary
This article explored the concepts of using the Cactus framework to write unit tests for testing servlets, JSPs, filters, TagLibs, and EJBs.References
Published August 5, 2004 Reads 18,240
Copyright © 2004 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Kishore Kumar
Kishore Kumar works as a Java architect at U.S. Technology (www.ustri.com). He specializes in J2EE applications.
![]() |
ashu 01/18/05 09:49:03 PM EST | |||
Really a good article. would be more helpfule if an EJB example is given. also steps till coding are given what happens after it (how to deploy through war etc) needs to be elaborated |
||||
![]() |
Nikhil 09/15/04 05:58:11 AM EDT | |||
This is an execellent article, It gives lot more information as compared to apache cactus website |
||||
- Kindle 2 vs Nook
- Why IBM’s Server Chief Got Busted
- Is Cloud Computing Like Teenage Sex?
- Industry Experts Discuss the State of Cloud Computing
- Performance Tuning Essentials for Java
- Confessions of a Ulitzer Addict
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- It's the Java vs. C++ Shootout Revisited!
- Cloud Computing Can Revitalize Your Career as Software Developer
- IBM Could "Reinvent" Java: Mills
- Oracle & Cloud Computing: Exclusive Q&A with SVP Richard Sarwal
- A Brief History of Cloud Computing
- Kindle 2 vs Nook
- Cloud CEOs, CTOs & SVPs to Speak at 4th International Cloud Computing Expo
- Why IBM’s Server Chief Got Busted
- Is Cloud Computing Like Teenage Sex?
- Industry Experts Discuss the State of Cloud Computing
- Performance Tuning Essentials for Java
- The Difference Between Web Hosting and Cloud Computing
- Cloud Computing Expo: Exclusive Q&A with Yahoo! SVP Cloud Computing
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- Confessions of a Ulitzer Addict
- My Thoughts on Ulitzer
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- The i-Technology Right Stuff
- JavaServer Faces (JSF) vs Struts
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- What's New in Eclipse?
- Why Do 'Cool Kids' Choose Ruby or PHP to Build Websites Instead of Java?
- i-Technology Predictions for 2007: Where's It All Headed?








































