System Testing a Java Web Service in Jetty

Automated system tests are an invaluable part of ensuring your software product is functioning as intended. On many of our projects, we are lucky enough to work with languages and frameworks that have a number of testing tools built around them, making it _relatively_ easy to create automated system tests.

However, there are times where the tools haven’t been made yet. Recently, we have been developing a large and complex Java web service for a client. We found that while we had access to a number of well-maintained testing libraries (JUnit, “Mockito”:http://code.google.com/p/mockito/, “Hamcrest”:http://code.google.com/p/hamcrest/), there weren’t any tools to create and run automated system tests against the web service.

So, we built a way to do it.

Our Java system tests had to be:

* Automated
* Configurable
* Instrumented

h2. Automated

In order to automate our system tests, we had to programmatically build and start the web service in Jetty. This was the most complex and difficult aspect of it. It required understanding _how_ Jetty starts and bootstraps web applications. We also had to make sure Jetty could be properly shutdown after the tests ran.

public static void startJetty() throws Exception {
        configureServerProperties();
 
        configureServer();
 
        configureWebAppContext();
 
        configureWebAppDeployer();
 
        server.addLifeCycle(webAppDeployer);
        server.addHandler(context);
        server.start();
 
        while (server.isStarting()) {
            Thread.sleep(250);
        }
 
        findInjector();
    }

All the gritty details can be found “here”:https://github.com/dewind/jetty-system-tests-example/blob/master/src/test/java/com/atomicobject/web/helpers/WebTestHelper.java#L112.

h2. Configurable

Our web service had to connect to a number of external devices and services, which would have made it very difficult to system test certain aspects of it. Using the power of “Guice”:http://code.google.com/p/google-guice/, we were able to swap-in “stubs”:http://en.wikipedia.org/wiki/Test_stub that simulate the external devices and services.

In order to do this we had to:

# Ensure that external services were wrapped in Java interfaces.
# Ensure that the concrete implementations of those interfaces were swappable with different ones.
# Start and bootstrap our web service with a different “module”:http://code.google.com/p/google-guice/wiki/GettingStarted containing our stubs. (See “here”:https://github.com/dewind/jetty-system-tests-example/blob/master/src/test/java/com/atomicobject/web/helpers/WebTestHelper.java#L68 and “here”:https://github.com/dewind/jetty-system-tests-example/blob/master/src/main/java/com/atomicobject/app/ApplicationContext.java.)

h2. Instrumented

Lastly, we wanted to be able to instrument various aspects of the application. For instance, we may want to create a number of entities in a database or alter the state of an object to trigger an event. We felt that the easiest way to do this would be to:

* Run Jetty in the same runtime as our tests.
* Expose our application context (a Guice injector) via a “ServletContextListener”:http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html.

Finding a context listener was a bundle of fun:

private static void findInjector() {
        EventListener[] eventListeners = context.getServletContext().getContextHandler().getEventListeners();
        for (EventListener eventListener : eventListeners) {
            if (eventListener instanceof ApplicationServletContextListener) {
                injector = ((ApplicationServletContextListener) eventListener).backDoorToInjector();
                break;
            }
        }
    }

Again, all the details can be found “here”:https://github.com/dewind/jetty-system-tests-example/blob/master/src/test/java/com/atomicobject/web/helpers/WebTestHelper.java#L132-L140.

h2. Example Java System Tests Project

We haven’t made this into a formal library (yet). However, an example project using all of the techniques described above has been put on GitHub and can be found “here”:https://github.com/dewind/jetty-system-tests-example. The project was built using “Maven”:http://maven.apache.org/ and “IntelliJ”:http://www.jetbrains.com/idea/.