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] _______________________________________________
