The geekier side of Riff Pie

Unit-testing RESTful Jersey services glued together with Spring

Monday, August 9th, 2010

So I’ve recently been playing around with building RESTful web services using Jersey, the production-quality reference implementation of JAX-RS. It’s a neat library, and, to my mind, simpler than other offerings such as Restlet.

However, this post isn’t yet another Jersey tutorial, there are millions of them already. I’ve found the quickest way to develop REST services is top-down, layer-by-layer, feature by feature, and it’s also one of those areas where test-driven development really shines. Luckily, Jersey comes with some good support for unit-testing, in the shape of the JerseyTest class, which leverages a bunch of container adapters to allow you to quickly and easily deploy resources to a container programmatically, and fire real HTTP requests at it. I use the excellent lightweight Grizzly container for this, it’s what works by default.

There’s a problem, though. I like wiring things together with Spring, and although Jersey provide a servlet for configuring your app with Spring, resources and all, it doesn’t actually let you get at the application context, so getting hold of beans from it is a no-no. Using Spring’s JUnit support doesn’t help, because both it, and the Jersey test, create separate, identical contexts. This means, if you need to use something from the context in your actual tests, you can’t. I want to. I like having my resources delegating down to a service layer, and I like mocking this layer out for some tests.

So after a few quickly-dead ideas involving awkward factory methods that let me reach into specific classes in the context, and a singleton servlet for testing – yuck – I had a dig about in the Jersey source code and came up with a little tweak, and subclassed JerseyTest to give trivially easy access to it. If you’ve used JerseyTest, you’ll be right at home, it works exactly the same way. Use the WebappDescriptor.Builder to configure your app, make sure your Spring context allows annotation configuration, and use Spring’s @Autowired in your test class to get any dependencies you need from your context.

Onwards with the obligatory example.

In the following test, I have a RESTful resource which delegates to an some service or other for some work. I want, for unit-testing purposes, to mock out this service. That means, both the RESTful service and the unit test need a reference to the same mocked service. Here’s the test class, with an instance variable declared to hold the service in question, annotated as Spring @Autowired.

public class JerseySpringTestSupportTest extends AbstractSpringAwareJerseyTest {
 
	public JerseySpringTestSupportTest() {
		super(new WebAppDescriptor.Builder("com.riffpie.common.testing.jersey.resources")
        .contextPath("testing")
        .contextParam("contextConfigLocation", "classpath:test-spring-jersey-integration-context.xml")
        .contextListenerClass(ContextLoaderListener.class)
        .build());
	}
 
        @Autowired
	private ArbitraryService service;
 
	@Test
	public void testServiceWorks() throws Exception {
 
		String input = "this is the input";
		String output = "THIS IS THE OUTPUT";
 
		Mockito.when(service.getEcho(input)).thenReturn(output);
 
		String response =
                          resource()
                          .path("echo")
                          .queryParam("input", input)
                          .accept(MediaType.TEXT_PLAIN)
                          .get(String.class);
 
		assertEquals(output, response);
 
		Mockito.verify(service).getEcho(input);
 
	}
}

And here’s the associated Spring config. I use Mockito to mock out a service – which I won’t patronise you by posting here – and inject it into the resource, which I also won’t post here.

 
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
 
    <context:annotation-config />
 
   	<bean id="testResource" class="com.riffpie.common.testing.jersey.resources.EchoResource">
   	  <property name="service" ref="delegate"/>
   	</bean>
 
	<bean id="delegate" factory-method="mock" class="org.mockito.Mockito">
	  <constructor-arg value="com.riffpie.common.testing.jersey.ArbitraryService"/>
	</bean>
 
</beans>

Your test instances are passed to the application context for auto-wiring, all the necessary dependencies are injected, and you’re away. Simples.

Well, if it’s source code you’re after, release 1.0 of the test class is available here. The source includes the actual classes used, as well as a more complete unit test than the one presented here. If you’re a Maven user, add my Maven repo http://maven.riffpie.com/releases/ to your POM, your settings.xml, repo manager or what have you, and use this snippet in your POM to use the library yourself. I’ve even included out-of-the-box support for cookies, which usually takes a bit of buggering about with Jersey clients.

  <dependency>
    <groupId>com.riffpie.common.testing</groupId>
    <artifactId>jersey-test-support</artifactId>
    <version>1.0</version>
  </dependency>