Installing Cactus

Installation involves the coordination of the two halves of the Cactus equation: the client side, where requests are composed and sent to the Web container, and the server side, where the tests are actually executed. (This is a simplified explanation. The exact relationship between the server and client sides of Cactus is explored in a later section, "Cactus Architecture.") The prerequisites for a Cactus installation are trivial (for anyone intending to write code that Cactus would test): a JVM capable of running JDK 1.2 and a servlet engine compliant with the Servlet 2.2 (or better) specification. Open-source development moves fast, and the Cactus user community constantly suggests improvements to the installation process. Furthermore, Cactus currently integrates with several tools, such as Ant, Maven, and Eclipse, to simplify the building and test-deployment process.


An overview of how to deploy and run Cactus tests with Ant is given in the section "Cactus with Ant."

Server-Side Installation

Installing Cactus components on the server involves the following steps:

  1. Put the Cactus libraries on the server's class path. In most cases, this means you must put these JAR files into the WEB-INF/lib folder of the Web app that will contain your Cactus tests.

  2. Put the test classes on the server's class path. This step is necessary because Cactus executes any given test case both on the server and on the client. Omitting this step will cause a ClassNotFoundException when the redirector servlet attempts to load a test case that is not on the server. It is important not only to put tests on the server initially but also to keep them up to date. If a test is changed and the server is not updated with a new copy of the test, it will continue to use the old one—causing unexpected results. For this reason, an automated build and deployment tool such as Ant is a practical necessity for development with Cactus.
  3. Put the classes under test on the server's class path. Naturally, the latest copy of all the code that the tests depend on also needs to reside on the server for the tests to run.
  4. Map URLs to the various Cactus redirectors. Ordinarily, doing so will involve adding mappings to the web.xml file of the Web app that contains the Cactus tests. Cactus operates by calling these redirectors with HTTP requests from the client; they must have URL mappings so they can be called externally. The following code listing shows the web.xml file that we will be using for the first example (assuming the Web app is named cactus-tests); its general form should be familiar to anyone who has worked with servlets before. This example includes mappings for the servlet, JSP custom tag, and filter redirectors.
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!DOCTYPE web-app
     PUBLIC "-//Oracle//DTD Web app 2.2//EN"
     <!— Mappings for the Servlet Redirector —>
     <!— Mappings for the FilterRedirector —>
     <!— Mappings for the JspRedirector —>

If custom tag tests are on the horizon, add redirector.jsp (the JSP redirector) to your Web app in the location specified in the previous step. (In the example, this will be the root of the Web app.) This step is necessary because the cactus JAR that contains the classes for the other redirectors cannot contain JSPs.

Client-Side Installation

Client-side setup involves less work. First, make sure that Cactus JARs are on the client's class path (along with your Cactus test cases, of course). Second, create a properties file called and put it on your class path. will contain entries that correspond to the URL of the test Web app and the names of the redirectors that were mapped during the server-side installation. Cactus needs these properties set so that it can direct requests to the server-side test runners from within the client test runners. Here are the contents of the file we will use to develop our first example:

cactus.contextURL=http://localhost:8080/cactus-tests cactus.servletRedirectorName=ServletRedirector cactus.jspRedirectorName=JspRedirector cactus.filterRedirectorName=FilterRedirector

The entries for the names of the redirectors are optional, and you can skip them if the redirectors are mapped in web.xml with the default names (i.e., ServletRedirector, JspRedirector, and FilterRedirector, respectively).


With some help from Ant, the file can be generated just before test suite execution. Generating the file allows for on-the-fly customization of the redirector URLs to account for differences between local and integration servers.

A Simple Example

Now that Cactus is installed in a Web app called cactus-tests on our Web server, let's try to run a test to prove that it works. The class we want to test is simple: It maps all the request parameter into session attributes (and returns a java.util.Map of the parameters and values). It has a single static method:

public static Map mapRequestToSession(HttpServletRequest request){
 HttpSession session = request.getSession();
 Map paramsMap = new HashMap();
 for(Enumeration e = request.getParameterNames(); e.hasMoreElements();){
 String paramName = (String)e.nextElement();
 String paramValue = request.getParameter(paramName);
 session.setAttribute(paramName, paramValue);
 paramsMap.put(paramName, paramValue);
 return paramsMap;

Our Cactus test for the SessionMapper class follows the standard JUnit test case template. It has a couple of differences: The class descends from org.apache.cactus.ServletTestCase, and there is another method in addition to the testSessionMapper()—beginSessionMapper(WebRequest request). Here is the code for the test case:

package xptoolkit.cactus;
import org.apache.cactus.*;
import junit.framework.*;
public class MapperTest extends ServletTestCase{
 /*standard constructor omitted */
 public void beginSessionMapper(WebRequest clientSideRequest){
 clientSideRequest.addParameter("xp", "rules!");
 public void testSessionMapper(){
 Map map = SessionMapper.mapRequestToSession(request);
 String val = (String)session.getAttribute("xp"); assertEquals("rules!", val);
 val = (String)map.get("xp");
 assertEquals("rules!", val);
 /*standard main and suite methods omitted */

The beginSessionMapper() method is executed on the client, and it sets up the request that will eventually arrive at the redirector servlet and thereby at the SessionMapper class. We'll cover this type of method in more detail in the next section. The testSessionMapper() method passes the request instance variable inherited from ServletTestCase into mapRequestToSession(HttpServletRequest request). The request variable implements the HttpServletRequest interface because it wraps an instance of the request generated by the Web server. Once mapRequestToSession() has completed its work, we expect to find a session attribute mapped under the key XP. ServletTestCase also provides an instance variable session (type HttpSession). We pull our value from this variable, assert that it is equal to the String we expect, and voilà! Our test is complete.

To execute the test, first we make sure that the server has the latest version of both SessionMapper and MapperTest. Once the server has been made aware of the new classes, we use one of the usual JUnit test runners to execute the test normally on the client side. When the test executes, Cactus automatically sends an HTTP request to the servlet redirector specifying that MapperTest should be instantiated and that its testMapRequestToSession() method should be invoked. The server runs the test and returns the results to the client for Cactus to interpret. Barring any setup issues, the test should run. As pair coding partners, we give each other a high-five and begin integrating our test and code into the system.