7.1 Introduction

Cactus, available from http://jakarta.apache.org/cactus, is an open source unit-testing framework for server side Java code. Specifically, Cactus allows you to test servlets, JSPs, and servlet filters.[1]

[1] Cactus may also be used to test Enterprise JavaBean code. This chapter does not discuss this technique. For more information please consult the Cactus documentation.

Cactus extends JUnit to provide three specific junit.framework.TestCase subclasses:

org.apache.cactus.ServletTestCase
org.apache.cactus.JspTestCase
org.apache.cactus.FilterTestCase

Each Cactus test case provides a specific function and is discussed in more detail in the following recipes. Cactus tests execute on both client and server. This is a significant departure from other testing frameworks and deserves some explanation. When using Cactus, you create a single subclass of one of the previously mentioned classes. Cactus then creates and runs two instances of your test case. One instance runs on the client JVM and the other runs inside of the servlet container's JVM. The client side allows HTTP headers and HTTP parameters to be added to the outgoing request. The server side invokes your servlet's methods, performs any necessary assertions, and sends back a response to the client. The client may then assert that the response contained the expected information.

It is important to know that you have to deploy your Cactus tests to the server. Specifically, you must create a web-application WAR file containing a valid web.xml file, all Cactus tests, and all support classes needed for your tests to execute. This is necessary because Cactus tests are executed on both the client and server. The recipes in this chapter delve into how this is done.

7.1.1 Implicit Objects

Each Cactus test case has a set of implicit objects. Implicit objects are only valid on the test case instance running in the server. These objects are used to set up information that a servlet expects to exist before invoking any methods to test. For example, you can use the config implicit object to set up initialization parameters. Here are the implicit objects defined by each test case:

org.apache.cactus.ServletTestCase

HttpServletRequestWrapper request
HttpServletResponse response
HttpSession session
ServletConfigWrapper config

org.apache.cactus.JspTestCase

PageContextWrapper pageContext
JspWriter out

org.apache.cactus.FilterTestCase

HttpServletRequestWrapper request
HttpServletResponse response
FilterConfigWrapper config
FilterChain filterChain

7.1.2 How Does It Work?

Cactus executes your tests on the client and server. This means two instances of your test case are created to run the tests. Figure 7-1 shows the execution of a Cactus test.

Figure 7-1. Execution of a Cactus test
figs/jexp_0701.gif

First, the JUnit test runner, executing in the client JVM, creates one instance of your test case. A redirector proxy executing in the server JVM creates the second instance. A redirector proxy is responsible for managing the server-side execution of a test. Let's walk through an example:

  1. The JUnit test runner instantiates your test case on the client and executes the runTest( ) method. For each testXXX( ) method, Cactus looks for an optional beginXXX(WebRequest) method. For example, if the test method is called testGetCustomerInformation( ), then Cactus looks for a method called beginGetCustomerInformation(WebRequest) to execute on the client.

    The beginXXX(WebRequest) allows for HTTP Parameters, HTTP Headers, Cookies, etc. to be added to the WebRequest object. This capability provides your test a chance to set up valid or invalid information for your servlet to handle.

  2. An HTTP connection is made with the server and a redirector is invoked. Cactus sends the WebRequest through the open connection to the server, too. This allows for the client to pass information to the servlet just like a typical HTTP request.

  3. The redirector proxy, executing on the server, takes control, instantiates a new instance of your test case, and sets up the appropriate implicit (depending on the Cactus test case). Only after the new instance is successfully constructed are the implicit objects valid.

    The implicit objects are only available on the server side instance of your test case. Accessing these objects in the client side test case causes a NullPointerException.

  4. The redirector invokes the setUp( ) method, followed by the testXXX( ) method.

  5. The testXXX( ) method must instantiate a new instance of your servlet and the call methods needed to execute your test.[2] JUnit assertion methods are used to test if the servlet's logic passed or failed. After the testXXX( ) method completes, the redirector calls the tearDown( ) method.

    [2] You, the unit test writer, must instantiate the servlet yourself. Cactus does not take on the role of a servlet container and therefore does not instantiate the servlet for you.

  6. The redirector proxy collects all test results and exceptions.

  7. Once all tests are complete, the information collected by the redirector proxy is sent back to the client.

  8. If a test did not fail, the optional endXXX(WebResponse) method is invoked (the one that matches the testXXX( ) method). For example, if you have a method called testGetCustomerInformation( ), then Cactus looks for a method called endGetCustomerInformation(WebResponse).

    The endXXX(WebResponse) method allows for the client to perform assertions on the information sent back by a servlet or JSP.