7.6 Writing a Cactus Test
7.6.1 Problem
You want to use Cactus to test server-side
code.
7.6.2 Solution
Extend the appropriate Cactus test-case class and implement one
or more testXXX( ),
beginXXX( ), and endXXX( )
methods.
7.6.3 Discussion
Cactus is a testing framework that extends JUnit to provide a way to
execute tests against code running in a server. Specifically, Cactus
allows for testing servlets, JSPs, and filters while running within a
servlet container.
There are seven main steps to writing a Cactus test.
Import the JUnit and Cactus classes: import org.apache.cactus.*;
import junit.framework.*;
Extend one of three Cactus test case classes:
- org.apache.cactus.ServletTestCase
-
Extend this class when you want to write unit tests for your
servlets. For example, if you need to test how a servlet handles
HttpServletRequest,
HttpServletResponse,
HttpSession, ServletContext, or
ServletConfig objects, write a
ServletTestCase:
public class TestMyServlet extends ServletTestCase {
}
- org.apache.cactus.JspTestCase
-
Extend this class when you want to write unit tests for your JSPs.
For example, if you need to test a custom tag library or
JspWriter, write a JspTestCase:
public class TestMyJsp extends JspTestCase {
}
- org.apache.cactus.FilterTestCase
-
Extend this class when you want to write unit tests for your filters.
For example, if you need to test that a
FilterChain or
FilterConfig object executes
correctly, write a FilterTestCase:
public class TestMyFilter extends FilterTestCase {
}
Implement the
JUnit setUp(
) and tearDown( ) methods. The
setUp( ) and tearDown( )
methods are optional methods that can be overridden by your test.
Unlike a normal JUnit test that executes these methods on the client,
Cactus executes these methods on the server. This allows you access
to the implicit objects defined by Cactus. Here is an example of
setting an attribute on the HttpSession implicit
object: public void setUp( ) throws Exception {
this.session.setAttribute("BookQuantity", new Integer(45));
}
public void tearDown( ) throws Exception {
this.session.removeAttribute("BookQuantity");
} As in JUnit, the setUp( ) and tearDown(
) methods are executed for each testXXX(
) method. The only twist is that Cactus executes these
methods on the server.
An idiom for
writing JUnit tests is to use the constructor instead of overriding
the setUp( ) method. This works because each
testXXX( ) method creates a new instantiation of
the test class, so instance variables are allocated for each test.
Here's an example of using the setUp(
) method:
public void setUp( ) {
this.myobject = new MyObject( );
}
Here's how the same thing can be accomplished using
the constructor:
public MyJUnitTest(String name) {
super(name);
this.myobject = new MyObject( );
}
This technique may cause problems with Cactus if you try to set up
information on any of the implicit objects. The implicit objects are
only valid on the server and are not initialized until after the
constructor is executed. The following code causes a
NullPointerException:
public MyCactusTest(String name) {
super(name);
// the implicit HttpSession is null
this.session.setAttribute("name","value");
}
Here's the correct solution:
public void setUp( ) {
// the implicit HttpSession is valid
this.session.setAttribute("name","value");
}
Be careful when using JUnit idioms because extensions of the JUnit
framework may behave quite differently than expected.
|
Implement the testXXX( ) methods. All Cactus test
methods are defined exactly the same as JUnit test methods. The only
difference is that Cactus test methods are executed on the server. In
the testXXX( ) method you: Here is an example:
public void testMyTest( ) throws Exception {
MyServlet servlet = new MyServlet( );
assertTrue(servlet.doSomethingThatEvaluatesToTrueOrFalse( ));
} Notice the explicit instantiation of the servlet. This is something
that servlet developers never have to do in a real web application
because the servlet container is responsible for the
servlet's lifecycle. In the Cactus world, you must
take on the role of the servlet container and ensure that the servlet
is instantiated, and if needed, initialized by invoking
init(ServletConfig) method.
Optionally, implement the beginXXX(WebRequest)
method. For each testXXX( ) method, you may define
a corresponding beginXXX( ) method. This method is
optional and executes on the client before the testXXX(
) method. Since this method executes on the client, the
implicit objects we had access to in the setUp( ),
tearDown( ), and testXXX( ) are
null.
Here is the full signature: public void beginXXX(org.apache.cactus.WebRequest) {
// insert client side setup code here
} This method can be used to initialize HTTP related information, such
as HTTP parameters, cookies, and HTTP headers. The values set here
become available in the testXXX( ) method through
the appropriate implicit object.
Optionally, implement the endXXX(WebResponse)
method. For each testXXX( ) method you may define
a corresponding endXXX( ) method. This method is
optional and executes on the client after the testXXX(
) method successfully completes. If the testXXX(
) method throws an exception this method is not executed. Cactus provides support for two
endXXX(WebResponse) signatures. Here are the valid
signatures:
public void endXXX(org.apache.cactus.WebResponse) {
// insert client side assertions
}
public void endXXX(com.meterware.httpunit.WebResponse) {
// insert client side assertions
} Use the first signature if you want to use the
WebResponse that comes with the Cactus framework.
This object provides a very simple view of the response. Use the
second signature if you want to use the
WebResponse object that is distributed with
HttpUnit. This object provides a detailed view of the response,
providing a much richer API. The Cactus framework accepts either
signature in your test with absolutely no effort on your part.
Finally, compile, deploy the web application to the server, and
execute the tests.
7.6.4 See Also
Recipe 7.3 describes how to set up a stable build
environment for server-side testing. For more information on using
HttpUnit to perform complex assertions in the
endXXX(WebResponse) method, see Chapter 5.
|