In a previous post, I mentioned using Jetty as part of testing a web application. The basic idea is that you can embed Jetty within your application (or at least within your test code), have your tests fire it up as required, and then perform “live” queries against your site. The purists would argue, of course, that this is “integration testing” as opposed to “unit testing,” since it typically requires infrastructure around your web app (database, etc.). I won’t argue with that. I will point out, however, that this can be very useful testing since it runs your code “end to end.” Thus, it can uncover things like REST URL’s not being mapped as you expect which might not be detected if you’re testing your REST controller classes in isolation.

So here’s how I go about using an embedded Jetty for testing.

The EmbeddedServer Class

Within the test portion of my project, I have a class named EmbeddedServer which looks like this:

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
import org.slf4j.bridge.SLF4JBridgeHandler;

public class EmbeddedServer
{
	private static Server server;

	public static final String TEST_CONTEXT = "/mySiteContext";
	public static final int TEST_PORT = 8080;

	public static void startIfRequired() throws Exception
	{
		if (server == null)
		{
			SLF4JBridgeHandler.removeHandlersForRootLogger();
			SLF4JBridgeHandler.install();
			
			System.setProperty("java.naming.factory.url.pkgs", "org.eclipse.jetty.jndi");
			System.setProperty("java.naming.factory.initial", "org.eclipse.jetty.jndi.InitialContextFactory");

			server = new Server(TEST_PORT);

			WebAppContext context = new WebAppContext();
			context.setDescriptor("src/main/webapp/WEB-INF/web.xml");
			context.setResourceBase("src/main/webapp");
			context.setContextPath(TEST_CONTEXT);
			context.setParentLoaderPriority(true);

			server.setHandler(context);

			server.start();
		}
	}
	
	public static void stop() throws Exception
	{
		if (server != null)
		{
			server.stop();
			server.join();
			server.destroy();
			server = null;
		}
	}

	public static void main(String[] args)
	{
		try
		{
			startIfRequired();
			server.join();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

This class manages a Jetty Server singleton, and provides startIfRequired and stop methods. The former will start the server if it’s not already started. For convenience, I also include a main method so that I can use this class to start a copy of the server in order to do manual testing.

A few items of interest:

  • The constant on line 9 establishes the servlet context for my site.
  • Lines 16 and 17 handle bridging the java.util.logging log utilities over to SLF4J. Jetty uses the former, but my logging is set up via logback, which uses the SLF4J API.
  • Lines 19 and 20 set some JNDI properties for the server.
  • Line 22 creates the server, using the port that I choose to test on.
  • Lines 24 through 28 handle setting up the context for my web app. This project I took this from uses Maven, so the paths are to the standard Maven locations for the web.xml file and the static resources.

Integrating EmbeddedServer with JUnit-Based Tests

With this in place, if I have an integration test that requires the site to be running, I simply make sure that it (or a base class) has the following:

	@BeforeClass
	public static void startServer() throws Exception
	{
		EmbeddedServer.startIfRequired();
	}

This makes sure that the server is running before any of the individual test cases are run. In many cases, there’s no reason this couldn’t have gone into a method annotated with @Before instead of @BeforeClass.

In certain situations, I have tests that end up using singletons. I try not to design with singletons, but sometimes I’m effectively forced into them by third-party libraries. In at least one case I can think of, in order to test error legs I was forced to manipulate the singleton in ways that have left it in an unstable state. This would obviously be A Bad Thing (“don’t cross the streams”) since it would potentially corrupt tests that came afterward. To handle that, I made sure that the singleton initialization code was tied to my app startup via a ServletContextListener, and that the code there fully initialized the singleton. Then, as part of the test that was a bit naughty, I simply included

	@AfterClass
	public static void stopServer() throws Exception
	{
		EmbeddedServer.stop();
	}

This way, the test shut down the server after it was done. Any subsequent set of tests would find the server down, and would start it up again, thus isolating the wicked things I did to the singleton as part of the test.

In theory one could do this with every test class. Starting up the server is a slow operation, however, so the idea was to only shut it down when absolutely necessary in order to shorten the amount of time the tests took to run.

The Tests Themselves

With that in place, it’s now very straightforward to execute actual HTTP transactions against the internal Jetty server using HttpUnit. Typically, I derive all of the HTTP-based test classes from a common base class, and then implement some utility methods thus:

	protected WebConversation createConversation()
	{
		HttpUnitOptions.setExceptionsThrownOnErrorStatus(false);
		HttpUnitOptions.setScriptingEnabled(false);

		WebConversation conversation = new WebConversation();
		conversation.getClientProperties()
					.setAutoRedirect(false);
		return conversation;
	}

	protected GetMethodWebRequest createGetRequest(String url, Map<String,String> params) 
		throws Exception
	{
		if (params != null)
		{
			String prefix = "?";
			
			StringBuilder builder = new StringBuilder();
			builder.append(url);
			
			for (Map.Entry<String, String> param : params.entrySet())
			{
				builder.append(prefix);
				builder.append(URLEncoder.encode(param.getKey(), "UTF-8"));
				builder.append("=");
				builder.append(URLEncoder.encode(param.getValue(), "UTF-8"));
				
				prefix = "&";
			}
			
			url = builder.toString();
		}
		GetMethodWebRequest request = new GetMethodWebRequest(url);
		return request;
	}

	protected WebResponse executeRequest(WebRequest request) throws Exception
	{
		/*
		 * Close connection after each transaction to prevent exhausting threads in server.
		 */
		request.setHeaderField("Connection", "Close");
		
		WebConversation conversation = createConversation();
		WebResponse response = conversation.getResponse(request);
		responses.add(response);
		return response;
	}

	protected WebResponse executeGet(String url, Map<String,String> params) throws Exception
	{
		return executeRequest(createGetRequest(url, params));
	}

The application from which I took this did not use cookies, so I could use a separate WebConversation for each transaction. If cookies were important, then one could easily modify things to use the same WebConversation for several transactions.

One issue I did run into during testing was the question of connections. By default, the way things are set up here, the unit test code will tend to create a new connection to the server for each individual GET request. Jetty, being a HTTP 1.1 server, will, by default, keep those connections open, and the HttpUnit library doesn’t aggressively close them. The upshot was that as the number of transactions in my unit tests increased, I eventually got to the point where the Jetty server had exhausted its internal thread pool, since a thread was being left behind waiting for a (possible) subsequent on each open connection.

The solution to that was line 42 – this adds a Connection: close header to each transaction, which requests Jetty to close the connection at the end of the response, thus keeping idle connections from accumulating. (There’s probably a way to force HttpUnit to close the connection as well, but it’s not obvious.)

Before anybody starts a debate on the subject, I should point out that I’m not arguing that HttpUnit is the best package out there for web site testing, but it suits my needs at present, since the site in question is primarily providing a REST interface, as opposed to an HTML one. Thus, a lower-level tool like HttpUnit works very well, and sometimes its easiest to stick with what you know. 🙂 If the site is primarily HTML-based, there are tools that are far more complete and adaptable, and quite possibly the site should be tested using a completely different approach, such as via Selenium.


This post originally appeared on SilverBayTech.com.