Testing RESTful Web Services made easy using the REST-assured Framework
October 23rd, 2011 by micha kops
There are many frameworks out there to facilitate testing RESTful webservices but there is one framework I’d like to acquaint you with my favourite framework named REST-assured.
REST-assured offers a bunch of nice features like a DSL-like syntax, XPath-Validation, Specification Reuse, easy file uploads and those features we’re going to explore in the following article.
With a few lines of code and Jersey I have written a RESTful web service that allows us to explore the features of the REST-assured framework and to run tests against this service.
Contents
- Prerequisites
- The REST Service to be tested
- Adding REST-assured to your Maven project
-
Examples
- Verify JSON GET Request
- Using JsonPath
- Using Groovy Closures
- Verifying XML
- XML using XPath
- XML verification vs a Schema
- Handling Request Parameters
- HTTP Status Code
- Authentication
- Setting HTTP Headers
- Verifying HTTP Headers
- Setting Cookies
- Verifying Cookies
- File Uploads
- Registering custom parsers for MIME-types
- Specification reuse
- Troubleshooting
- Tutorial Sources
- Slideshow
- Resources
Prerequisites
We’re going to need a JDK and Maven .. nothing more …
The REST Service to be tested
I have added a demo web application that exposes a RESTful service (Jersey used here) and allows us to run our tests against it. There are two possible ways to run the web app:
- Check out the tutorial sources (see chapter “Tutorial Sources Download“) and run
mvn tomcat:run
- Or simply download the following war file from my Bitbucket repository and deploy it to a valid web container like Tomcat, Jetty etc..
- If you’re running the app and open the URL http://localhost:8080 in your browser you should be able to see a nice overview of the exported service methods
Adding REST-assured to your Maven project
You only need to add the following dependencies to your pom.xml to use REST-assured and – of course JUnit..
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>com.jayway.restassured</groupId> <artifactId>rest-assured</artifactId> <version>1.4</version> <scope>test</scope> </dependency>
Examples
I have added some examples for different scenarios to test .. headers, status codes, cookies, file uploads etc ..
Verify JSON GET Request
We’re testing a simple response containing some JSON data here ..
- Request URL: /service/single-user
- Request Method: GET
- Response Content-Type: application/json
- Response Body:
{ "email":"test@hascode.com", "firstName":"Tim", "id":"1", "lastName":"Testerman" }
And this is our test case:
@Test public void testGetSingleUser() { expect(). statusCode(200). body( "email", equalTo("test@hascode.com"), "firstName", equalTo("Tim"), "lastName", equalTo("Testerman"), "id", equalTo("1")). when(). get("/service/single-user"); }
Using JsonPath
This time we’re using JsonPath to programatically test the returned JSON structure..
- Request URL: /service/single-user
- Request Method: GET
- Response Content-Type: application/json
- Response Body
{ "email":"test@hascode.com", "firstName":"Tim", "id":"1", "lastName":"Testerman" }
And this is our test:
@Test public void testGetSingleUserProgrammatic() { Response res = get("/service/single-user"); assertEquals(200, res.getStatusCode()); String json = res.asString(); JsonPath jp = new JsonPath(json); assertEquals("test@hascode.com", jp.get("email")); assertEquals("Tim", jp.get("firstName")); assertEquals("Testerman", jp.get("lastName")); assertEquals("1", jp.get("id")); }
Using Groovy Closures
JsonPath allows us to use Groovy closures to perform searches on the returned JSON structure.
- Request URL: /service/persons/json
- Request Method: GET
- Response Content-Type: application/json
- Response Body
{ "person":[ { "@id":"1", "email":"test@hascode.com", "firstName":"Tim", "lastName":"Testerman" },{ "@id":"20", "email":"dev@hascode.com", "firstName":"Sara", "lastName":"Stevens" },{ "@id":"11", "email":"devnull@hascode.com", "firstName":"Mark", "lastName":"Mustache" } ] }
And this is our test – we’re searching for a person whose email matches the pattern /test@/:
@Test public void testFindUsingGroovyClosure() { String json = get("/service/persons/json").asString(); JsonPath jp = new JsonPath(json); jp.setRoot("person"); Map person = jp.get("find {e -> e.email =~ /test@/}"); assertEquals("test@hascode.com", person.get("email")); assertEquals("Tim", person.get("firstName")); assertEquals("Testerman", person.get("lastName")); }
Verifying XML
Now we’re going to validate returned XML
- Request URL: /service/single-user/xml
- Request Method: GET
- Response Content-Type: application/xml
- Response Body
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <user> <email>test@hascode.com</email> <firstName>Tim</firstName> <id>1</id> <lastName>Testerman</lastName> </user>
And this is our test:
@Test public void testGetSingleUserAsXml() { expect(). statusCode(200). body( "user.email", equalTo("test@hascode.com"), "user.firstName", equalTo("Tim"), "user.lastName", equalTo("Testerman"), "user.id", equalTo("1")). when(). get("/service/single-user/xml"); }
XML using XPath
To validate complex XML structure XPath is way more comfortable here..
- Request URL: /service/persons/xml
- Request Method: GET
- Response Content-Type: application/xml
- Response Body
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <people> <person id="1"> <email>test@hascode.com</email> <firstName>Tim</firstName> <lastName>Testerman</lastName> </person> <person id="20"> <email>dev@hascode.com</email> <firstName>Sara</firstName> <lastName>Stevens</lastName> </person> <person id="11"> <email>devnull@hascode.com</email> <firstName>Mark</firstName> <lastName>Mustache</lastName> </person> </people>
And this is our test:
@Test public void testGetPersons() { expect() .statusCode(200) .body(hasXPath("//*[self::person and self::person[@id='1'] and self::person/email[text()='test@hascode.com'] and self::person/firstName[text()='Tim'] and self::person/lastName[text()='Testerman']]")) .body(hasXPath("//*[self::person and self::person[@id='20'] and self::person/email[text()='dev@hascode.com'] and self::person/firstName[text()='Sara'] and self::person/lastName[text()='Stevens']]")) .body(hasXPath("//*[self::person and self::person[@id='11'] and self::person/email[text()='devnull@hascode.com'] and self::person/firstName[text()='Mark'] and self::person/lastName[text()='Mustache']]")) .when().get("/service/persons/xml"); }
XML verification vs a Schema
Now we’re going to validate the xml returned against a XML schema file
- Request URL: /service/single-user/xml
- Request Method: GET
- Response Content-Type: application/xml
- Response Body
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <user> <email>test@hascode.com</email> <firstName>Tim</firstName> <id>1</id> <lastName>Testerman</lastName> </user>
This is the schema we’re using to validate in a file named user.xsd
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema"> <element name="user"> <complexType> <sequence> <element name="email"> <simpleType> <restriction base="string"> <pattern value=".+@.+"></pattern> </restriction> </simpleType> </element> <element name="firstName" type="string"></element> <element name="id" type="int"></element> <element name="lastName" type="string"></element> </sequence> </complexType> </element> </schema>
And this is our test case:
@Test public void testGetSingleUserAgainstSchema() { InputStream xsd = getClass().getResourceAsStream("/user.xsd"); assertNotNull(xsd); expect(). statusCode(200). body( matchesXsd(xsd)). when(). get("/service/single-user/xml"); }
Handling Request Parameters
This is a simple example how to add some request parameters
- Request URL: /service/user/create
- Request Method: GET
- Response Content-Type: application/json
- Response Body
{ "email":"test@hascode.com", "firstName":"Tim", "id":"1", "lastName":"Testerman" }
And this is our test:
@Test public void testCreateuser() { final String email = "test@hascode.com"; final String firstName = "Tim"; final String lastName = "Tester"; given(). parameters( "email", email, "firstName", firstName, "lastName", lastName). expect(). body("email", equalTo(email)). body("firstName", equalTo(firstName)). body("lastName", equalTo(lastName)). when(). get("/service/user/create"); }
HTTP Status Code
Now an example how to verify HTTP headers – in the following example, a 404 Page Not Found is returned ..
- Request URL: /service/status/notfound
- Request Method: GET
- Response Content-Type: text/plain
- Response Status: 404 / Page Not Found
And this is our test:
@Test public void testStatusNotFound() { expect(). statusCode(404). when(). get("/service/status/notfound"); }
Authentication
In this example we’re handling basic authentication ..
- Request URL: /service/secure/person
- Request Method: GET
- Response Content-Type: text/plain
- Response Status: 401 Unauthorized/ 200 Status Ok (when logged in with username=admin and password=admin)
And this is our test:
@Test public void testAuthenticationWorking() { // we're not authenticated, service returns "401 Unauthorized" expect(). statusCode(401). when(). get("/service/secure/person"); // with authentication it is working expect(). statusCode(200). when(). with(). authentication().basic("admin", "admin"). get("/service/secure/person"); }
Setting HTTP Headers
In the following example we’re setting some HTTP headers. The value of the HTTP header named “myparam” is returned by the REST service in the response body..
- Request URL: /service/single-user
- Request Method: GET
- Response Content-Type: text/plain
- Response Body: #value-of-myparam#
And this is our test:
@Test public void testSetRequestHeaders() { expect(). body(equalTo("TEST")). when(). with(). header("myparam", "TEST"). get("/service/header/print"); expect(). body(equalTo("foo")). when(). with(). header("myparam", "foo"). get("/service/header/print"); }
Verifying HTTP Headers
Now we’re going to verify HTTP response headers
- Request URL: /service/header/multiple
- Request Method: GET
- Response Content-Type: text/plain
- Response Header: customHeader1:foo, anotherHeader:bar
And this is our test:
@Test public void testReturnedHeaders() { expect(). headers("customHeader1", "foo", "anotherHeader", "bar"). when(). get("/service/header/multiple"); }
Setting Cookies
The following example shows how to set cookies. The REST service sends a 403 / Forbidden until a cookie with name=authtoken and value=abcdef is send.
- Request URL: /service/access/cookie-token-secured
- Request Method: GET
- Response Content-Type: application/json
- Response Status: 403 / 200
And this is our test:
@Test public void testAccessSecuredByCookie() { expect(). statusCode(403). when(). get("/service/access/cookie-token-secured"); given(). cookie("authtoken", "abcdef"). expect(). statusCode(200). when(). get("/service/access/cookie-token-secured"); }
Verifying Cookies
This is how to verify cookies set by the service. The service returns the request parameter “name” as the value of the cookie named “userName”:
- Request URL: /service/cookie/modify
- Request Method: GET
- Request Parameter: name
- Response Cookie: userName:#value-of-name#
And this is our test:
@Test public void testModifyCookie() { expect(). cookie("userName", equalTo("Ted")). when(). with().param("name", "Ted"). get("/service/cookie/modify"); expect(). cookie("userName", equalTo("Bill")). when(). with().param("name", "Bill"). get("/service/cookie/modify"); }
File Uploads
The following example shows how to handle file uploads. We’re sending a text file to the REST service and the service returns the file content as a string in the response body.
- Request URL: /service/file/upload
- Request Method: GET
- Request Content-Type: multipart/form-data
- Response Content-Type: text/plain
- Response Body: #file-content#
And this is our test:
@Test public void testFileUpload() { final File file = new File(getClass().getClassLoader() .getResource("test.txt").getFile()); assertNotNull(file); assertTrue(file.canRead()); given(). multiPart(file). expect(). body(equalTo("This is an uploaded test file.")). when(). post("/service/file/upload"); }
Registering custom parsers for MIME-types
Sometimes you’ve got to handle a RESTful service that returns an invalid content type so that REST-assured does not know which parser to use to process the response. This is not a real problem though because the framework allows you to register parsers for a given content type as shown in the example below:
- Request URL: /service/detail/json
- Request Method: GET
- Response Content-Type: text/json
- Response Body:
{"test":true}
And this is our test:
@Test public void testRegisterParserForUnknownContentType() { RestAssured.registerParser("text/json", Parser.JSON); expect(). body("test", equalTo(true)). when(). get("/service/detail/json"); }
Specification reuse
Another nice feature of the REST-assured framework the possibility to create specifications and reuse, modify or extend them in several tests.
- Request URL: /service/single-user / /service/user/create
- Request Method: GET
- Response Content-Type: application/json
- Response Body
{ "email":"test@hascode.com", "firstName":"Tim", "id":"1", "lastName":"Testerman" }
And this is our test:
@Test public void testSpecReuse() { ResponseSpecBuilder builder = new ResponseSpecBuilder(); builder.expectStatusCode(200); builder.expectBody("email", equalTo("test@hascode.com")); builder.expectBody("firstName", equalTo("Tim")); builder.expectBody("lastName", equalTo("Testerman")); builder.expectBody("id", equalTo("1")); ResponseSpecification responseSpec = builder.build(); // now we're able to use this specification for this test expect(). spec(responseSpec). when(). get("/service/single-user"); // now re-use for another test that returns similar data .. you may // extend the specification with further tests as you wish final String email = "test@hascode.com"; final String firstName = "Tim"; final String lastName = "Testerman"; expect(). spec(responseSpec). when(). with(). parameters( "email", email, "firstName", firstName, "lastName",lastName). get("/service/user/create"); }
Troubleshooting
- “WARNING: Cannot find parser for content-type: text/json — using default parser.” – The framework does not know for sure which parser to use. Register the corresponding parser like this: e.g. RestAssured.registerParser(“text/json”, Parser.JSON);
- “If I use the War file, the path is no longer localhost:8080, but becomes http://localhost:8080/rest-assured-example/.” - specify the changed context path in the get() method or – more comfortable – define it as a global setting e.g. like this one
@Before public void setUp(){ RestAssured.basePath = "yourbasepath"; }
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/rest-assured-samples
Slideshow
Resources
Tags: groovy, jaxb, jaxrs, jersey, rest, rest-assured, tomcat, webservice, ws


January 24th, 2012 at 9:53 pm
That’s really nice. How do you get the exported view? Is that a default Jersey feature? I know the wadl is produced, but didn’t know this part. Thanks for your examples too. Very helpful. We are trying to work out whether to use something like this, jbehave, or custom….
February 8th, 2012 at 6:50 pm
THANKS!!!!
May 10th, 2012 at 1:53 am
Where can I find the demo application war file?
May 10th, 2012 at 6:37 am
Hi, you can download the war file from my Bitbucket repository.
May 10th, 2012 at 7:37 pm
Thanks for the .war. I didn’t see it when I was looking at your Bitbucket repository. Now that I’ve got the .war deployed and tomcat running, I tried your example for testGetSingleUser(). I had to change get(“/service/single-user”); to get(“/rest-assured-example/service/single-user”); in order for it to find the resource and return a status code of 200.
May 16th, 2012 at 3:49 pm
Running the XML using XPath example, I’m getting an error:
java.lang.AssertionError: Body doesn’t match.
Expected:
an XML document with XPath //person[@id='1']/email[.='test@hascode.com'] and firstName=’Tim’ and lastName=’Testerman’
Actual:
test@hascode.comTimTestermandev@hascode.comSaraStevensdevnull@hascode.comMarkMustache
I’m using this code
expect().statusCode(200)
.body(hasXPath(“//person[@id='1']/email[.='test@hascode.com'] and firstName=’Tim’ and lastName=’Testerman’”))
.body(hasXPath(“//person[@id='20']/email[.='dev@hascode.com'] and firstName=’Sara’ and lastName=’Stevens’”))
.body(hasXPath(“//person[@id='11']/email[.='devnull@hascode.com'] and firstName=’Mark’ and lastName=’Mustache’”))
.when().get(“/rest-assured-example/service/persons/xml”);
Thanks for any help you can provide so I can understand how this works.
May 16th, 2012 at 7:23 pm
Works for me .. using mvn tomcat:run as well as using a deployed war archive on a tomcat instance. but I’ve noticed that the xpath expression was not well defined and precise enough .. I’ve fixed this and updated the example .. thanks for mentioning!
May 16th, 2012 at 9:02 pm
Thanks Micha. The new code works.
June 4th, 2012 at 2:11 pm
Hello, Micha. Excellent tutorial. If I use the War file, the path is no longer localhost:8080, but becomes http://localhost:8080/rest-assured-example/. This causes the tests to fail. How do I fix this? I am currently looking at your source code to find it.
Thanks so much !!
June 4th, 2012 at 4:58 pm
Thanks! You just simply need to specify the new context path – one way is to specify it in the get() method but it is way more comfortable to specify it globally using RestAssured.basePath = “yourbasepath”;
June 6th, 2012 at 8:49 pm
Hi again, Micha. I should have found that answer right above.
Anyway, any ideas why I am
unable to do your public void testFileUpload() on JUnit? Everything else works.
June 7th, 2012 at 8:22 am
No problem
Are you getting any error here? Does the file exist in your classpath? Have you modified the original example somehow? Does the servlet container return any error or is there an error displayed in your server’s logs?
June 7th, 2012 at 1:30 pm
final File file = new File( getClass().getClassLoader()
.getResource( “test.txt” ).getFile() );
java.lang.NullPointerException
at it.RestAssuredSampleServiceIT.testFileUpload(RestAssuredSampleServiceIT.java:154)
I see the file in the project as you listed it, and have not changed anything.
June 7th, 2012 at 5:47 pm
Hmm.. could you please run the test using some external file and an absolute path? Have you included the dependency for jersey-multipart (com.sun.jersey.contribs:jersey-multipart) in your project?
June 7th, 2012 at 6:05 pm
Micha, I don’t know why but I searched and the solution was to move the test.txt to the \target\classes folder.
June 7th, 2012 at 6:29 pm
Ok – I’m glad it finally worked somehow
January 10th, 2013 at 3:05 pm
Hi Micha
I took the last war file and deployed it on Tomcat 7, I get a 404 error for every REST call from your welcome test page.
Tomcat is “Apache Tomcat 7.0.30 Server”, installed as a service on Windows. Java is “C:\Program Files\Java\jre7\bin\client\jvm.dll”
I took the same war file, and installed it on another machine with Tomcat “Apache Tomcat 6.0.36 “, Java “C:\Program Files\Java\jre7\bin\client\jvm.dll”, everything works fine.
Does anybody had the same problem ?
January 10th, 2013 at 6:54 pm
Hi,
what context path did you use for the application? I bet it differs from the one used in my tutorial
Best regards
Micha
February 18th, 2013 at 7:45 am
Hi, Micha,
Thanks for putting together such a nice tutorial! It is really helpful for me to understand rest-assured better. I am trying to figure out what framework to use for my rest web service testing and have some further questions on rest-assured for you.
Do you happen to have an example to use rest-assured for https web service?
Also for a web service that supports both xml and json, is there any way to write same test to handle both these two response format? If not, the tests will double
.
Also for a test case, say, 1. user A (may be an admin user) logs in, 2. A create a user B, 3. A then retrieves user B’s info to verify that user B is created correctly.
step 2 and 3 will need to send out the cookies returned from step 1. Will tester have to set cookie in each request? i was hopping that rest-assured will support updating some default cookies based on server response, then the next http request will by default send the updated cookies. Do you happen to know if this is supported by rest-assured?
Thanks in advance for your help.
Lisa