Dawid Weiss
JUnit-Tomcat: No Mocking Just Testing
http://www.cs.put.poznan.pl/dweiss/xml/projects/junit-tomcat/index.xml
Motivation and Rough Design Overview

There are a few approaches to servlet container components testing. Let's mention the two most popular:

  1. mock objects, which simulate the API and functionality of a container, but run locally;
  2. server-side testing, when the tests are spawned on the server side and their results are transmitted back to the test aggregation facility;

In the first case we use a cheap substitute of web application container's functionality so testing more complex interaction among server side components (such as request forwarding, combined filters or such) heavily depends on the quality of mock objects implementation. The obvious gain is that we can run the tests locally, without complex configuration.

In the second scenario we test directly on the deployment server. While such tests are very valuable and can help detect a vast majority of server-configuration and software problems, they also require costly configuration and deployment on each test run -- something the developers dislike.

This package is a simple attempt to create a middle ground solution: a framework which would allow running tests locally, with minimum amount of configuration/ setup effort and at the same time in a realistic container environment. There should be a possibility to run the same set of tests on the deployment server.

The implementation is as follows:

  • Tests are written as simple JUnit tests which are given the deployment URL of a running Web application. This implies that tests are external (functional) and communicate with the application only by using standard HTTP.
  • Each test can be isolated (run on a freshly deployed application).
  • Test instances are bound to a given URL by wrapping them using a decorator. A local decorator is provided which starts and stops the embedded web application container (Tomcat 5.5.x) and instructs tests about the local URL to the tested application. A remote decorator permits reuse of the same test cases (suites) on a final deployment server.
How to use it

Write a JUnit (version 4.0 is not supported at the moment) test case. Each test should communicate with a servlet, JSP or any other part of the tested application. For example (we use HttpUnit [outlink] for communicating with the server using HTTP protocol):

import com.meterware.httpunit.*;
import com.dawidweiss.junittomcat.TomcatAwareTestCase;

public class SimpleServletTest extends TomcatAwareTestCase {

    public SimpleServletTest(String tname) {
        super(tname);
    }

    public void testHelloResponse() throws Exception {
        final WebConversation wc = new WebConversation();
        final WebRequest request = new GetMethodWebRequest(
        	getTestContextURL() + "/SimpleServlet");
        final WebResponse response = wc.getResponse(request);
        assertEquals("Hello", response.getText());
    }
}
1. A simple class testing a servlet.

Note a few things in the above:

  • The test class extends TomcatAwareTestCase. This is a superclass which implements ITomcatAware interface through which the decorator pushes the deployment URL of the application to be tested. This URL can be acquired from the superclass by calling getTestContextURL() method (but of course you can implement the interface yourself).
  • Everything else is a JUnit test case (assertions, method naming convention, etc.).

To run the test using a local web container you need to wrap the above test in a decorator and provide a few details concerning Tomcat's setup:

public class LocalhostTestSuite extends TestSuite {

    public static Test suite() {
        final TestSuite suite = new TestSuite();

        final File catalinaHome = new File("catalina");
        final String host = "localhost";
        final int port = 8081;
        final boolean testIsolation = true;

        suite.addTest(
                new EmbeddedTomcatTestCaseDecorator(
                        new TestSuite(SimpleServletTest.class), 
                        host, port, "/contextPath", 
                        new File("contextLocation"), testIsolation, catalinaHome));
        return suite;
    }
}
2. A TestSuite decorating the test case to use an embedded, locally spawned Tomcat container.

Decorator parameters are required and allow flexibility in the setup of your Web application. For example, you can test your application with various port and context path combinations (very good for detecting those nasty hardcoded URLs). Test isolation parameter controls how the test is executed -- passing true runs each test method using a separate, fresh web application context. Otherwise the same application is reused for all tests in the wrapped test suite (this way is a lot faster, but may break test independence).

Once you're done coding locally, you can re-run your tests against a remote server (it must be deployed manually though). For example:

public class ProductionTestSuite extends TestSuite {

    public static Test suite() {
        final TestSuite suite = new TestSuite();

        suite.addTest(
                new RemoteServerTestCaseDecorator(
                        new TestSuite(SimpleServletTest.class), 
                        "http://production.server.com/contextPath"));
        return suite;
    }
}
3. A different decorator uses the same test case, but runs it against a remote server.
Your Tomcat Tests in Eclipse

The actual motivation behind this project was to run a set of simple servlet/ filter tests in a local environment under Eclipse (and from ANT). Simple unit tests, no container configuration-deployment-undeployment cycle. Works for me :)

Servlet tests in Eclipse's JUnit runner.
Download Links

This is an initial release. If it catches on, I'll move it to a more sensible repository.


(c) Dawid Weiss. All rights reserved unless stated otherwise.