
Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:
Once the early-adopter seats are all used, the price will go up and stay at $33/year.
Last updated: January 11, 2025
REST-assured was designed to simplify the testing and validation of REST APIs and is highly influenced by testing techniques used in dynamic languages such as Ruby and Groovy.
The library has solid support for HTTP, starting of course with the verbs and standard HTTP operations, but also going well beyond these basics.
In this guide, we are going to explore REST-assured and we’re going to use Hamcrest to do assertion. If you’re not already familiar with Hamcrest, you should first brush up with the tutorial: Testing with Hamcrest.
Also, to learn about more advanced use cases of REST-assured, check out our other articles:
Now let’s dive in with a simple example.
Before we get started, let’s ensure that our tests have the following static imports:
io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
org.hamcrest.Matchers.*
We’ll need that to keep tests simple and have easy access to the main APIs.
Now, let’s get started with a simple example – a basic betting system exposing some data for games:
{
"id": "390",
"data": {
"leagueId": 35,
"homeTeam": "Norway",
"visitingTeam": "England",
},
"odds": [{
"price": "1.30",
"name": "1"
},
{
"price": "5.25",
"name": "X"
}]
}
Let’s say that this is the JSON response from hitting http://localhost:8080/events?id=390 — the locally deployed API.
Let’s now use REST-assured to verify some interesting features of the response JSON:
@Test
public void givenUrl_whenSuccessOnGetsResponseAndJsonHasRequiredKV_thenCorrect() {
get("/events?id=390").then().statusCode(200).assertThat()
.body("data.leagueId", equalTo(35));
}
So, what we did here is verify that a call to the endpoint /events?id=390 responds with a body containing a JSON String whose leagueId of the data object is 35.
Let’s have a look at a more interesting example. Let’s say we would like to verify that the odds array has records with prices 1.30 and 5.25:
@Test
public void givenUrl_whenJsonResponseHasArrayWithGivenValuesUnderKey_thenCorrect() {
get("/events?id=390").then().assertThat()
.body("odds.price", hasItems("1.30", "5.25"));
}
If we’re using Maven, we add the rest-assured dependency in the pom.xml file:
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
REST-assured takes advantage of the power of Hamcrest matchers to perform its assertions, so we must include the hamcrest dependency as well:
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.1</version>
</dependency>
Consider an array that comprises primitives rather than objects:
[1, 2, 3]
This is called an anonymous JSON root, meaning that it has no key-value pair, nevertheless, it is still valid JSON data.
We can run validation in such a scenario by using the $ symbol or an empty String (“”) as the path. Assume we expose the above service through http://localhost:8080/json — then we can validate it with REST-assured:
when().get("/json").then().body("$", hasItems(1, 2, 3));
This approach does the same:
when().get("/json").then().body("", hasItems(1, 2, 3));
When we start using REST-assured to test our REST services, we need to understand that floating point numbers in JSON responses are mapped to primitive type float.
The use of float type is not interchangeable with double:
{
"odd": {
"price": "1.30",
"ck": 12.2,
"name": "1"
}
}
If we run the following test for the value of ck:
get("/odd").then().assertThat().body("odd.ck", equalTo(12.2));
This test will fail even if the value we are testing is equal to the value in the response. This is because we’re comparing to a double rather than to a float.
To make it work, we have to explicitly specify the operand to the equalTo matcher method as a float:
get("/odd").then().assertThat().body("odd.ck", equalTo(12.2f));
Typically, we would perform a request by calling a method such as get(), corresponding to the request method we want to use.
In addition, we can also specify the HTTP verb using the request() method:
@Test
public void whenRequestGet_thenOK(){
when().request("GET", "/users/eugenp").then().statusCode(200);
}
The example above is equivalent to using get() directly.
Similarly, we can send HEAD, CONNECT, and OPTIONS requests:
@Test
public void whenRequestHead_thenOK() {
when().request("HEAD", "/users/eugenp").then().statusCode(200);
}
POST requests also follow a similar syntax, and we can specify the body by using the with() and body() methods.
Therefore, to create a new Odd by sending a POST request, we can run:
@Test
public void whenRequestedPost_thenCreated() {
with().body(new Odd(5.25f, 1, 13.1f, "X"))
.when()
.request("POST", "/odds/new")
.then()
.statusCode(201);
}
The Odd object sent as body will automatically be converted to JSON. We can also pass any String that we want to send as our POST body.
We can configure a lot of default values for the tests:
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://api.github.com";
RestAssured.port = 443;
}
Here, we’re setting a base URI and port for our requests. Besides these, we can also configure the base path, root path, and authentication.
Note that we can also reset to the standard REST-assured defaults by using:
RestAssured.reset();
Let’s see how we can measure the response time using the time() and timeIn() methods of the Response object:
@Test
public void whenMeasureResponseTime_thenOK() {
Response response = RestAssured.get("/users/eugenp");
long timeInMS = response.time();
long timeInS = response.timeIn(TimeUnit.SECONDS);
assertEquals(timeInS, timeInMS/1000);
}
Note that, in the above example:
We can also validate the response time – in milliseconds – with the help of a simple long Matcher:
@Test
public void whenValidateResponseTime_thenSuccess() {
when().get("/users/eugenp").then().time(lessThan(5000L));
}
If we want to validate the response time in a different time unit, then we’ll use the time() matcher with a second TimeUnit parameter:
@Test
public void whenValidateResponseTimeInSeconds_thenSuccess(){
when().get("/users/eugenp").then().time(lessThan(5L),TimeUnit.SECONDS);
}
Not only can it validate a JSON response, it can validate XML as well.
Let’s assume we make a request to http://localhost:8080/employees and we get the XML response:
<employees>
<employee category="skilled">
<first-name>Jane</first-name>
<last-name>Daisy</last-name>
<sex>f</sex>
</employee>
</employees>
Given this response, let’s verify that the first-name is Jane:
@Test
public void givenUrl_whenXmlResponseValueTestsEqual_thenCorrect() {
post("/employees").then().assertThat()
.body("employees.employee.first-name", equalTo("Jane"));
}
We can also verify that all values match our expected values by chaining body matchers together:
@Test
public void givenUrl_whenMultipleXmlValuesTestEqual_thenCorrect() {
post("/employees").then().assertThat()
.body("employees.employee.first-name", equalTo("Jane"))
.body("employees.employee.last-name", equalTo("Daisy"))
.body("employees.employee.sex", equalTo("f"));
}
Or we can use the shorthand version with variable arguments:
@Test
public void givenUrl_whenMultipleXmlValuesTestEqualInShortHand_thenCorrect() {
post("/employees")
.then().assertThat().body("employees.employee.first-name",
equalTo("Jane"),"employees.employee.last-name",
equalTo("Daisy"), "employees.employee.sex",
equalTo("f"));
}
We can also verify our responses using XPath. Consider an example that executes a matcher on the first-name:
@Test
public void givenUrl_whenValidatesXmlUsingXpath_thenCorrect() {
post("/employees").then().assertThat().
body(hasXPath("/employees/employee/first-name", containsString("Ja")));
}
XPath also accepts an alternate way of running the equalTo matcher:
@Test
public void givenUrl_whenValidatesXmlUsingXpath2_thenCorrect() {
post("/employees").then().assertThat()
.body(hasXPath("/employees/employee/first-name[text()='Jane']"));
}
First, let’s see how to log entire request details using log().all():
@Test
public void whenLogRequest_thenOK() {
given().log().all()
.when().get("/users/eugenp")
.then().statusCode(200);
}
This will log something like:
Request method: GET
Request URI: https://api.github.com:443/users/eugenp
Proxy: <none>
Request params: <none>
Query params: <none>
Form params: <none>
Path params: <none>
Multiparts: <none>
Headers: Accept=*/*
Cookies: <none>
Body: <none>
To log only specific parts of the request, instead of all(), we can call params(), body(), headers(), cookies(), method(), path():
@Test
public void whenLogRequest_thenOK() {
given().log().params()
.when().get("/users/eugenp")
.then().statusCode(200);
}
Note that other libraries or filters used may alter what’s actually sent to the server, so this should only be used to log the initial request specification.
Similarly, we can log the response details.
In the following example, we’re logging the response body only:
@Test
public void whenLogResponse_thenOK() {
when().get("/repos/eugenp/tutorials")
.then().log().body().statusCode(200);
}
Sample output:
{
"id": 9754983,
"name": "tutorials",
"full_name": "eugenp/tutorials",
"private": false,
"html_url": "https://github.com/eugenp/tutorials",
"description": "The \"REST With Spring\" Course: ",
"fork": false,
"size": 72371,
"license": {
"key": "mit",
"name": "MIT License",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
...
}
We also have the option of logging the response only if an error occurs or the status code matches a given value:
@Test
public void whenLogResponseIfErrorOccurred_thenSuccess() {
when().get("/users/eugenp")
.then().log().ifError();
when().get("/users/eugenp")
.then().log().ifStatusCodeIsEqualTo(500);
when().get("/users/eugenp")
.then().log().ifStatusCodeMatches(greaterThan(200));
}
We can also log both request and response only if our validation fails:
@Test
public void whenLogOnlyIfValidationFailed_thenSuccess() {
when().get("/users/eugenp")
.then().log().ifValidationFails().statusCode(200);
given().log().ifValidationFails()
.when().get("/users/eugenp")
.then().statusCode(200);
}
In this example, we want to validate that the status code is 200. Only if this fails, the request and response will be logged.
In this tutorial, we’ve explored the REST-assured framework and looked at its most important features that we can use to test our RESTful services and validate their responses.