REST-assured vs Jersey-Test-Framework: Testing your RESTful Web-Services

September 5th, 2011 by

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 SOAP 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 Bitbucket repository – download it there or check it out using Mercurial:

hg clone https://bitbucket.org/hascode/hascode-tutorials

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

http://localhost:8080/rest-test-tutorial/user/id/12

Tags: , , , , , , , , , , ,

14 Responses to “REST-assured vs Jersey-Test-Framework: Testing your RESTful Web-Services”

  1. Johan Says:

    Great blog post! As the founder of REST Assured I just want to point out that the real benefit of this framework comes to light when we’re doing a bit more complicated tests. It’s very simple to do e.g. authentication, file uploading, re-use of specifications across multiple tests and its ability to use Groovy lambda expressions when validating complex JSON or XML documents. I’ll hopefully blog about the latter in a not so distance future.

    Regards,
    /Johan

  2. micha kops Says:

    thanks! i’ve fallen in love with this framework in one project where I had to write excessive tests for several RESTful webservices delivering JSON and XML. rest-assured made my day(s) :)
    i am planning to write another article with some examples that have helped me a lot in the past.

    Please keep up the great work with rest-assured (and of course powermock!) :)

  3. Rahul Says:

    Hi,

    I was using JerseyTest for the REST testing but want to shift to Rest assured but i am getting a few issues.

    Running Jersey Test was done by
    mvn clean install ;
    The above will compile the code, run the tests in the embedded grizzly container, and run the tests.
    However for RESTAssured, i need to start the server and then run tests from eclipse, which is not something i want to do, the reason being that there are some reports of code coverage etc generated which are dependent on the tests which are run, and so i want it to be part of the build.

    But if i just try to run “mvn clean install”, RESTAssured tries to find the server at localhost:8080, and fails.
    Is there a way to accomplish it?
    Thanks

  4. Ladu kishore Dash Says:

    Hi,
    When I am trying to build the above example I am getting compilation failure, I am using maven 2 and open jdk 1.6

    INFO] ————————————————————————
    [ERROR] BUILD FAILURE
    [INFO] ————————————————————————
    [INFO] Compilation failure

    /NotBackedUp/ldash/software/Rest-assured/rest-test-tutorial/src/main/java/com/hascode/tutorial/rest/UserService.java:[11,1] annotations are not supported in -source 1.3
    (use -source 5 or higher to enable annotations)
    @Path(“/user”)

    /NotBackedUp/ldash/software/Rest-assured/rest-test-tutorial/src/main/java/com/hascode/tutorial/rest/User.java:[7,1] annotations are not supported in -source 1.3
    (use -source 5 or higher to enable annotations)
    @XmlRootElement

    [INFO] ————————————————————————
    [INFO] Trace
    org.apache.maven.BuildFailureException: Compilation failure
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:715)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
    at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
    at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
    at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
    Caused by: org.apache.maven.plugin.CompilationFailureException: Compilation failure
    at org.apache.maven.plugin.AbstractCompilerMojo.execute(AbstractCompilerMojo.java:516)
    at org.apache.maven.plugin.CompilerMojo.execute(CompilerMojo.java:114)
    at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:490)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
    … 17 more
    [INFO] ————————————————————————
    [INFO] Total time: 2 minutes 19 seconds
    [INFO] Finished at: Fri Dec 02 16:57:09 IST 2011
    [INFO] Final Memory: 22M/167M

  5. Ladu kishore Dash Says:

    Hi,
    When I am trying to build the above example I am getting compilation failure, I am using maven 2 and open jdk 1.6. Could you please have a look at it let me know where is the problem.

    Thanks in advance

    INFO] ————————————————————————
    [ERROR] BUILD FAILURE
    [INFO] ————————————————————————
    [INFO] Compilation failure

    /NotBackedUp/ldash/software/Rest-assured/rest-test-tutorial/src/main/java/com/hascode/tutorial/rest/UserService.java:[11,1] annotations are not supported in -source 1.3
    (use -source 5 or higher to enable annotations)
    @Path(“/user”)

    /NotBackedUp/ldash/software/Rest-assured/rest-test-tutorial/src/main/java/com/hascode/tutorial/rest/User.java:[7,1] annotations are not supported in -source 1.3
    (use -source 5 or higher to enable annotations)
    @XmlRootElement

    [INFO] ————————————————————————
    [INFO] Trace
    org.apache.maven.BuildFailureException: Compilation failure
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:715)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
    at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
    at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
    at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
    Caused by: org.apache.maven.plugin.CompilationFailureException: Compilation failure
    at org.apache.maven.plugin.AbstractCompilerMojo.execute(AbstractCompilerMojo.java:516)
    at org.apache.maven.plugin.CompilerMojo.execute(CompilerMojo.java:114)
    at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:490)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
    … 17 more
    [INFO] ————————————————————————
    [INFO] Total time: 2 minutes 19 seconds
    [INFO] Finished at: Fri Dec 02 16:57:09 IST 2011
    [INFO] Final Memory: 22M/167M

  6. micha kops Says:

    You should advise maven to use Java 6 .. just add the following snippet to you pom.xml:
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.0.2</version>
    <configuration>
    <source>1.6</source>
    <target>1.6</target>
    </configuration>
    </plugin>
    </plugins>

  7. Ladu kishore Dash Says:

    Hey Micha it works for me now thanks a lot man.

  8. Ladu kishore Dash Says:

    Now while trying to run the test case from eclipse I am getting the following error
    java.lang.NoClassDefFoundError: groovyx/net/http/HTTPBuilder
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2444)
    at java.lang.Class.getDeclaredMethods(Class.java:1808)

    I have added all the necessary jar in the build path (groovy1.7.11.jar,junit.jar ,rest-assured-1.4.jar etc),Please suggest what could be the problem.

  9. micha kops Says:

    Do you have the Maven Plugin for Eclipse installed? And if so – is your project “mavenized”?

  10. Ladu Kishore Dash Says:

    yes I am having the maven plugin for eclipse but I was building the war file(for rest service) from command prompt through “mvn clean install”, and for testing the functionality I was running the test case from eclipse.

    And when I am trying to run the test from command prompt using “mvn -Dtest=UserServiceTestUsingRestAssured test” I am getting the following error
    Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.073 sec <<< FAILURE!
    testUserFetchesSuccess(com.hascode.tutorial.rest.UserServiceTestUsingRestAssured) Time elapsed: 0.055 sec <<< ERROR!
    java.lang.NoClassDefFoundError: antlr/collections/AST
    at org.codehaus.groovy.antlr.AntlrParserPluginFactory.createParserPlugin(AntlrParserPluginFactory.java:27)
    at org.codehaus.groovy.control.SourceUnit.parse(SourceUnit.java:234)
    at org.codehaus.groovy.control.CompilationUnit$1.call(CompilationUnit.java:157)

  11. Ladu kishore Dash Says:

    Hi Micha please ignore my previous messages, Now I have “Mavenized” my project it’s working fine.

  12. Ladu kishore Dash Says:

    But when I am trying to ran the test cases from Linux I am getting the following error
    estUserFetchesSuccess(com.hascode.tutorial.rest.UserServiceTestUsingRestAssured) Time elapsed: 0.153 sec <<< ERROR!
    java.lang.NoClassDefFoundError: org/objectweb/asm/Opcodes
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:277)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:212)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)

    and testUserNameNotFound(com.hascode.tutorial.rest.UserServiceTestUsingRestAssured) Time elapsed: 0 sec <<< ERROR!
    java.lang.NoClassDefFoundError: Could not initialize class com.jayway.restassured.RestAssured
    at com.hascode.tutorial.rest.UserServiceTestUsingRestAssured.testUserNameNotFound(UserServiceTestUsingRestAssured.java:52)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    Do I need to change something on pom file for groovy and rest-assured?

  13. micha kops Says:

    org.objectweb.asm.Opcodes is included twice .. one is in the “rest-assured” lib .. it’s asm:asm:3.2 .. in the dependency for “jersey-server” there’s also a dependency to the asm lib (asm:asm:3.1) so the class should definitely be there .. but I think the two dependencies are the reason that asm-3.1 is used .. perhaps you can resolve the error by fixing the version by exclusion in your pom.xml

    And if you’re using rest-assured solely .. please take a look at the following tutorial of mine :)

  14. Ladu Kishore Dash Says:

    Thanks for the reply, I have gone through your tutorial,It’s really a good one :-)

Leave a Reply

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 80,798 bad guys.