Unit tests with a "Real" Server
Normally, I like to make my unit tests so that they do not depend on external things running. As a general rule, I try to write tests that prove that the logic in my code works. While I care that communication between my application and, say, a database or application server works, it’s not my code that’s doing the communication, so I don’t tend to write unit tests that cover that. If I’m using something like Apache Jakarta Commons’ HttpClient to do some kind of communication via HTTP, I tend to write tests that confirm that the request (PostMethod or the like) is built properly by my code and that my code handles a return result correctly. With some layering it’s possible to structure you code so that these sorts of tests are easy to execute without actually doing the communication.
While I care that the HttpClient does what it’s supposed to, my unit tests tend to be more concerned with whether or not my code does it what it’s supposed to. Sometimes I will write some unit tests for frameworks that I use, but I tend to try and keep these tests isolated. But that’s a story for later.
The other day, I was inspired to write some unit tests that actually do the live communication between my Eclipse plug-in and an application server via HTTP. Setting this up is surprisingly easy using the Jetty and OSGi HTTP service bundles that’re already included with the workbench.
I added the following two methods to my JUnit 4 test class:
@BeforeClass public static void startServer() throws Exception { Dictionary settings = new Hashtable(); settings.put(“other.info”, SERVER_NAME); settings.put(“http.port”, 0); JettyConfigurator.startServer(SERVER_NAME, settings);This code creates an HTTP server, asking the server to find an available port (which my client code needs to complete the communication) and then registers some servlets.ServiceReference[] reference = Activator.getDefault().getBundle().getBundleContext().getServiceReferences("org.osgi.service.http.HttpService", "(other.info=usagedata.upload.tests)"); Object assignedPort = reference[0].getProperty("http.port"); port = Integer.parseInt((String)assignedPort); tracker = new ServiceTracker(Activator.getDefault().getBundle().getBundleContext(), reference[0], null); tracker.open(); HttpService server = (HttpService)tracker.getService(); server.registerServlet(GOOD_SERVLET_NAME, new GoodServlet(), null, null); server.registerServlet(BAD_SERVLET_NAME, new BadServlet(), null, null);
}
@AfterClass public static void stopServer() throws Exception { tracker.close(); JettyConfigurator.stopServer(SERVER_NAME); }
Some bundles have to be added to my test fragment (I tend to build unit tests for plug-ins in fragments):
- javax.servlet
- org.eclipse.equinox.http.jetty
- org.eclipse.osgi.services
I’m still not convinced that this is exactly what I want to do (I certainly have some Law of Demeter issues to work out with it), but it works well. The best part is that my unit tests can be easily automated since they don’t have to depend on some external thing being properly configured.