Find below a documentation about how to run unit tests against JAX-RS REST 
endpoints as part of the Maven test phase. This documentation is also available 
on the wikihttp://opencast.jira.com/wiki/display/MH/Unit+Testing+REST+Endpoints

Hope that helps,
Christoph



h1. Overview

This document explains how to unit test JAX-RS based REST endpoints as part of 
the normal Maven test phase.

Matterhorn's communication between nodes relies on REST endpoints, so carefully 
testing them is crucial. Looking through the code base I found that tests are 
only available as part of the integration tests in matterhorn-test-harness. 
This should change. Testing them as part of the normal maven test cycle seems 
better to deliver solid code. Here's how.
Matterhorn's communication between nodes relies on REST endpoints, so carefully 
testing them is crucial. Looking through the code base I found that tests are 
only available as part of the integration tests in matterhorn-test-harness. 
This should change. Testing them as part of the normal Maven test phase seems 
better to deliver solid code. Here's how.

The solution I'd like to present makes use of _Jersey_ 
([http://jersey.java.net/]), the JAX-RS reference implementation, and the 
wonderful DSL _rest-assured_ ([http://code.google.com/p/rest-assured/]) for 
testing REST endpoints in a concise manner.

h1. Prerequisites

Add the following dependencies to your module's pom.xml
{code:language=html/xml}
<!-- Testing -->
<dependency>
 <!-- Use junit-dep - instead of junit - which does not include hamcrest
      since otherwise it leads to method-not-found collisions with the
      rest-assured library. -->
 <groupId>junit</groupId>
 <artifactId>junit-dep</artifactId>
 <version>4.10</version>
 <scope>test</scope>
 <exclusions>
   <exclusion>
     <groupId>org.hamcrest</groupId>
     <artifactId>hamcrest-core</artifactId>
   </exclusion>
 </exclusions>
</dependency>
<dependency>
 <groupId>com.sun.jersey</groupId>
 <artifactId>jersey-bundle</artifactId>
 <version>1.6</version>
 <scope>test</scope>
</dependency>
<dependency>
 <groupId>com.jayway.restassured</groupId>
 <artifactId>rest-assured</artifactId>
 <version>1.6.1</version>
 <scope>test</scope>
</dependency>
<!-- Include this dependency to run tests inside the IntelliJ IDE.
    Maven picks it up from matterhorn-common dependencies. -->
<!--
<dependency>
 <groupId>org.apache.httpcomponents</groupId>
 <artifactId>httpcore</artifactId>
 <version>4.1.4</version>
 <scope>test</scope>
</dependency>
-->
{code}

h1. Writing a Test

When writing a test you need to be aware of some pitfalls. The most annoying 
one is that Jersey scans the whole class path for JAX-RS annotated classes and 
instantiates them. That means no-arg constructors and that you have to think 
about how necessary dependencies make it into your service class. In normal 
operation OSGi does this for you. I haven't checked if it's possible to 
substitute the OSGi environment with a DI framework to take this job but anyway 
I don't like this approach. If you split up your service into a business and a 
dependency part it's easier and more conscious to connect to dependencies under 
different circumstances.

h2. REST Service Design

{code:language=java}
// no @Path annotation here since this class cannot be created by JAX-RS. Put 
it on implementations.
public abstract class AbstractUserRestService {
 protected abstract UserService getUserService();

 @PUT
 @Path("/user")
 public Response putUser(@FormParam("name") final String name) {
   ...
 }

 @GET
 @Produces(MediaType.APPLICATION_JSON)
 @Path("/user/{name}")
 public Response getUser(@PathParam("name") final String name) {
   ...
 }
}

// OSGi implementation
@Path("/")
public class UserRestService extends AbstractUserRestService {
 private UserService us;

 /** OSGi callback. */
 public void activate(ComponentContext cc) {
 }

 /** OSGi callback. */
 public void deactivate() {
 }

 /** OSGi callback. */
 public void setUserService(UserService us) {
   this.us = us;
 }

 @Override protected UserService getUserService() {
   return us;
 }
}

// Unit test implementation
// use base path /test to prevent conflicts with the production service
@Path("/test")
// put @Ignore here to prevent Maven surefire from complaining about missing 
test methods
@Ignore
public class TestUserRestService extends AbstractUserRestService {
 // Declare this dependency static since the TestUserRestService gets 
instantiated multiple times.
 // Haven't found out who's responsible for this but that's the way it is.
 // This is particularly neccessary if the UserService uses persistent storage. 
If it's not static
 // you cannot add something in one test method, then retrieve it in another 
one if you use
 // an in-memory database as backend.
 public static final UserService us = new UserServiceImpl();

 @Override protected UserService getUserService() {
   return us;
 }
}
{code}

h2. Test Case

An example test case using the _rest-assured_ library now may look something 
like this. Please see the documentation 
[http://code.google.com/p/rest-assured/wiki/Usage] for details about using 
_rest-assured_.

{code:language=java}
import static com.jayway.restassured.RestAssured.*;
import static com.jayway.restassured.matcher.RestAssuredMatchers.*;
import static org.hamcrest.Matchers.*;

public class UserRestServiceTest {
 @Test
 public void testPutUser() {
   given().formParam("name", "klaus")
           .expect()
           .statusCode(CREATED)
           .header(LOCATION, "http://localhost:8080/test/users/klaus";)
           .when().put("/test/user");
 }

 @Test
 public void testGetUser() {
   expect().statusCode(OK)
           .body("name", equalTo("klaus"))
           .when().get("/test/user/klaus");
 }

 private static HttpServer hs;

 // Great. Checkstyle: "This method should no be static". JUnit: "Method 
setUp() should be static." ;)
 // CHECKSTYLE:OFF
 @BeforeClass
 public static void setUp() throws Exception {
   System.out.println("Start http server");
   hs = HttpServerFactory.create("http://localhost:8080/";);
   hs.start();
 }

 @AfterClass
 public static void tearDown() {
   System.out.println("Stop http server");
   hs.stop(0);
 }
 // CHECKSTYLE:ON
}
{code}
_______________________________________________
Matterhorn mailing list
[email protected]
http://lists.opencastproject.org/mailman/listinfo/matterhorn


To unsubscribe please email
[email protected]
_______________________________________________

Reply via email to