Tuesday, January 4, 2011

Unit Test Your REST Service

Here's an approach for unit-testing your RESTful service. The moving parts include:

  1. The REST service to be tested
  2. A standalone CXF-based server that hosts the REST service
  3. A Spring configuration that wires the REST service together with its dependencies and makes it available to the standalone server
  4. The unit test

Let's start with a simple REST service:


public interface MyService {
@GET
@Path("info")
String getInfo();
}

What this service does is irrelevant for purposes of this post; I'm keeping things simple so I can expose just the basic mechanics. In either event, let's assume there's an implementation of this interface available named "MyServiceImpl", and use that in the standalone CXF-based server:


public class StandaloneRestService {
private JAXRSServerFactoryBean sf;
public StandaloneRestService(MyService restSvc) {
sf = new JAXRSServerFactoryBean();
sf.setResourceClasses(MyServiceImpl.class);
sf.setResourceProvider(MyServiceImpl.class,
new SingletonResourceProvider(restSvc));
sf.setAddress("http://localhost:9000/info/");
sf.create();
}
}

Yes, I know - I handed off the interface but the standlone server is also aware of the implementation. For now, I'm not going to worry about it; this is not production-level, it's just a demo.

The REST service itself might rely on various other components, e.g. a DAO, a business-level service POJO, etc. - and as you can see, the standalone server relies on the REST service. Continuing to ignore irrelevant details here, let's assume Spring is used to wire things together - the REST service with its dependencies, and the REST service gets handed off to the standalone server. The Spring runtime constructs that standalone server, and it will then be available at the advertised IP:port for testing purposes (when you run mvn test). There's no magic here, it's just standard Spring; as such, I won't go into those details. What's worth mentioning are the underlying dependencies - as per the examples offered in the CXF download, here's the general set of jar needed:

  • org.apache.cxf:cxf-rt-transports-http
  • org.apache.cxf:cxf-rt-transports-http-jetty
  • org.apache.cxf:cxf-rt-frontend-jaxrs
  • org.apache.cxf:commons-httpclient
  • javax.ws.rs:jsr311-api

Adjust as needed depending on your environment, versions in play, etc. Finally, the unit test, with various pieces gratuitously lifted from the CXF samples:


@ContextConfiguration(locations = "spring-rest-test.xml")
public class RestTest extends AbstractTestNGSpringContextTests {

@Test(enabled = true)
public void confirmInfo() throws Exception {

System.out.println("confirmServices");
URL url = new URL("http://localhost:9000/info");
InputStream in = url.openStream();
String response = getStringFromInputStream(in);
assert response != null;
// add other assertions and tests as needed...
}

private String getStringFromInputStream(InputStream in) throws Exception {
CachedOutputStream bos = new CachedOutputStream();
IOUtils.copy(in, bos);
in.close();
bos.close();
return bos.getOut().toString();
}
}

Now you'll be able to write meaningful unit tests that can be run in your nightly builds, instead of a heavy-handed manual approach to developing your REST services.

No comments:

Post a Comment