Today we’re going to take a look at two specific frameworks that enables you to efficiently test your REST-ful services: On the one side there is the framework REST-assured that offers a nice DSL-like syntax to create well readable tests – on the other side there is the Jersey-Test-Framework that offers a nice execution environment and is built upon the JAX-RS reference implementation, Jersey.
In the following tutorial we’re going to create a simple REST service first and then implement integration tests for this service using both frameworks.
The title of this article might be misleading due to the fact that I am not going to compare both frameworks to choose a winner, just showing the different approach ..
Prerequisites
Only JDK and Maven needed here …
Creating a new Maven project
A new Maven project is our first step for the following tutorial ..
-
Create a new Maven project with your favourite IDE and Maven plugin installed or via console using
mvn archetype:generate
-
That’s all for now :)
The REST Service to be tested
First we need a REST service to write some tests for .. luckily that’s done with a few steps using JAX-RS ..
-
First we’re adding some dependencies for Jersey, JAX-B and Jersey-JSON to our pom.xml
<dependencies> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-server</artifactId> <version>1.9</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.2.4</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.2.4</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId> <version>1.9</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-json</artifactId> <version>1.9</version> </dependency> </dependencies> <repositories> <repository> <id>maven2-repository.dev.java.net</id> <name>Java.net Repository for Maven</name> <url>http://download.java.net/maven/2/</url> <layout>default</layout> </repository> <repository> <id>maven-repository.dev.java.net</id> <name>Java.net Maven 1 Repository (legacy)</name> <url>http://download.java.net/maven/1</url> <layout>legacy</layout> </repository> </repositories>
-
In the next step we’re creating our exported service method in a class named UserService
package com.hascode.tutorial.rest; import java.util.Date; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("/user") public class UserService { @GET @Produces(MediaType.APPLICATION_JSON) @Path("/id/{id}") public User findById(@PathParam("id") final Long id) { if (id.equals(666l)) { return null; } final User user = new User(); user.setId(id); user.setFirstName("Tim"); user.setLastName("Tester"); user.setBirthday(new Date(1321009871)); return user; } }
-
The service exports a User object as a JSON structure .. this is done via @Produces(MediaType.APPLICATION_JSON)
-
Now we need the user object .. the mapping to JSON is done using JAX-B – the only thing we need is one annotation, @XmlRootElement
package com.hascode.tutorial.rest; import java.util.Date; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class User { private Long id; private String firstName; private String lastName; private Date birthday; // getter + setter }
-
Finally we’re adding the following web.xml to the directory src/main/webapp/WEB-INF
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>Jersey REST Servlet</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>com.hascode.tutorial.rest</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Jersey REST Servlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
-
Now we’re starting the rest service with an embedded tomcat instance using
mvn tomcat:run
-
We’re able to run the REST service by calling the following url http://localhost:8080/rest-test-tutorial/user/id/12
-
The service returns the following JSON code
{ "birthday":"1970-01-16T07:56:49.871+01:00", "firstName":"Tim", "id":"12", "lastName":"Tester" }
-
Keep the embedded tomcat process running .. we’re going to need it for our integration tests later ..
Testing the Service
Now that we’ve got a nice, running REST service we want to test the exported service methods and data we’re getting from the service..
Using REST-assured
The first candidate for writing an integration test here is REST-assured ….
-
First we’re adding the dependencies for the REST-assured framework to our pom.xml
<dependency> <groupId>com.jayway.restassured</groupId> <artifactId>rest-assured</artifactId> <version>1.2.3</version> </dependency>
-
That’s what our integration test looks like
package com.hascode.tutorial.rest; import static com.jayway.restassured.RestAssured.expect; import static com.jayway.restassured.RestAssured.get; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; import org.junit.Test; public class UserServiceTestUsingRestAssured { @Test public void testUserFetchesSuccess() { expect(). body("id", equalTo("12")). body("firstName", equalTo("Tim")). body("lastName", equalTo("Tester")). body("birthday", equalTo("1970-01-16T07:56:49.871+01:00")). when(). get("/rest-test-tutorial/user/id/12"); } @Test public void testUserNotFound() { expect(). body(nullValue()). when(). get("/rest-test-tutorial/user/id/666"); } }
-
For more detailed information on the testing and matcher api, take a look at the documentation on the rest-assured website
-
That what my JUnit Runner in Eclipse looks like ;)
-
Update: I have written a complete and detailed tutorial covering the different features of the REST-assured framework and added a RESTful web service to run the tests from the tutorial against it: “Testing RESTful Web Services made easy using the REST-assured framework”
Using Jersey-Test-Framework
Now let’s try Jersey ..
-
We need some dependencies for the jersey-test-framework so we’re adding them to our pom.xml
<dependency> <groupId>com.sun.jersey.jersey-test-framework</groupId> <artifactId>jersey-test-framework-core</artifactId> <version>1.9</version> <scope>test</scope> </dependency> <dependency> <groupId>com.sun.jersey.jersey-test-framework</groupId> <artifactId>jersey-test-framework-external</artifactId> <version>1.9</version> </dependency>
-
That’s how our integration test looks like
package com.hascode.tutorial.rest; import static org.junit.Assert.assertEquals; import java.net.URISyntaxException; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.junit.Test; import com.sun.jersey.api.client.UniformInterfaceException; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.test.framework.AppDescriptor; import com.sun.jersey.test.framework.JerseyTest; import com.sun.jersey.test.framework.WebAppDescriptor; public class UserServiceTestUsingJerseyTestFramework extends JerseyTest { @Override protected AppDescriptor configure() { return new WebAppDescriptor.Builder().build(); } @Test public void testUserFetchesSuccess() throws JSONException, URISyntaxException { WebResource webResource = client().resource("http://localhost:8080/"); JSONObject json = webResource.path("/rest-test-tutorial/user/id/12") .get(JSONObject.class); assertEquals("12", json.get("id")); assertEquals("Tim", json.get("firstName")); assertEquals("Tester", json.get("lastName")); assertEquals("1970-01-16T07:56:49.871+01:00", json.get("birthday")); } @Test(expected = UniformInterfaceException.class) public void testUserNotFound() { WebResource webResource = client().resource("http://localhost:8080/"); JSONObject json = webResource.path("/rest-test-tutorial/user/id/666") .get(JSONObject.class); } }
-
More detailed information on the jersey-test-framework can be found at its project website or Naresh’ blog. The real strength of the Jersey-Test-Framework lies in its capability to start different types of containers to create a test environment for your rest service – we don’t use this nice feature here and could have also simply created a Jersey client using Client client = Client.create() and parsed the response …
-
Running the sample code in your IDE your JUnit view might look like this one
Tutorial Sources
I have put the source from this tutorial on my GitHub repository – download it there or check it out using Git:
git clone https://github.com/hascode/rest-test-tutorial.git
Troubleshooting
-
“Caused by: com.sun.jersey.api.MessageException: A message body writer for Java class …, and Java type class…, and MIME media type application/json was not found” – The dependency for jersey-json is missing .. just add the following dependency to your pom.xml
<dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-json</artifactId> <version>1.9</version> </dependency>
Resources
Additional REST articles of mine
Please feel free to have a look at these tutorials of mine covering different aspects of handling or creating RESTful webservices.
Article Updates
-
2015-08-06: Links to other REST articles of mine added.
-
2015-10-22: Link list updated.