REST API Testing with REST-assured
Learn to perform automated API testing with REST-assured. We will walk through all important concepts with easy-to-follow examples and code snippets.
- 1. Introduction
- 2. Setting Up with Maven
- 3. Testing with REST-assured
- 3.1. Setting Default Host, Port and API Root
- 3.2. GET APIs
- 3.3. POST APIs
- 3.4. PUT APIs
- 3.5. DELETE APIs
- 4. Building Request Headers and Body
- 4.1. Request Headers
- 4.2. Request Body
- 5. Verifying Response Status and Body
- 6. Extracting Response data
- 7. Logging Response Data
- 8. Measuring Response Time and Testing Timeouts
- 9. Conclusion
REST-assured is an excellent tool for writing automated tests in BDD style development, although it can be used with other styles. It helps in testing a real or mock REST API in a declarative manner, inspired by other dynamic languages such as Ruby and Groovy.
REST-assured has excellent support for testing HTTP methods and uses methods with names of HTTP verbs, such as get() and post() etc. The general syntax for writing a test is:
As a best practice, always add the following static import statements in the test class so we can use all methods directly. These will include almost all the necessary methods for sending the requests and verifying the response with Hamcrest matchers.
import static io.restassured.RestAssured.*; import static io.restassured.matcher.RestAssuredMatchers.*; import static org.hamcrest.Matchers.*;
REST-assured depends on the following modules to work with:
- rest-assured and rest-assured-common: consist of core classes and interfaces.
- json-path: makes it easy to parse JSON documents. It uses Groovy’s GPath syntax.
- json-schema-validator: helps in validating the JSON response structure if we want to.
- xml-path: makes it easy to parse XML documents.
- groovy: if groovy is not present in the classpath, already.
io.rest-assured rest-assured-common 5.1.1 test io.rest-assured rest-assured 5.1.1 test io.rest-assured json-path 5.1.1 test io.rest-assured xml-path 5.1.1 test org.codehaus.groovy groovy 3.0.12
If we are using Java 9+ then we can include all required dependencies with single inclusion using rest-assured-all.
io.rest-assured rest-assured-all 5.1.1 test
3. Testing with REST-assured
Let us learn the basics of testing APIs using examples.
3.1. Setting Default Host, Port and API Root
Although, we can specify the complete API URL, including hostname and port, in each test method but it may not be appropriate for clean code. REST-assured supports baseURI, port and basePath static variables from class RestAssured to configure common URI parts in a single location.
For example, we are building our examples using the public APIs available at reqres. It’s all APIs start with URL https://reqres.in/api/ so we can define the base URI and API path as follows:
@BeforeAll public static void setup() < baseURI = "https://reqres.in/"; basePath = "/api"; >
The GET API details are as follows:
Use get() method to invoke the API with path parameters. To verify the response data, we can use the inbuilt methods such as equalsTo(), hasItems(), is() etc.
given() .pathParam("id", 2) .when() .get("/users/") .then() .statusCode(equalTo(200)) .body("data.id", equalTo(2)) .body("data.email", equalTo("janet.weaver@reqres.in"));
The demo POST API, we are using the following request.
To send a POST request with the body, we can send use body() method. To verify the response, we can use the methods as discussed in the previous section.
@Test public void createUserWithCustomObject_thenSuccess() throws JSONException < given() .body(new User("lokesh", "admin@howtodoinjava.com")) .header(new Header("x-custom-header", "value")) .contentType("application/json") .when() .post("/users") .then() .statusCode(201) .body("id", notNullValue()) .body("name", equalTo("lokesh")) .body("email", equalTo("admin@howtodoinjava.com")); >
For the demo PUT API, we are using the following request. We are updating the email address of employee whose id is 2 .
Sending a PUT request is similar to POST request.
@Test public void updateUser_thenSuccess() < given() .body(new User("john", "john@howtodoinjava.com")) .contentType("application/json") .when() .put("/users/2") .then() .statusCode(200) .body("name", equalTo("john")) .body("email", equalTo("john@howtodoinjava.com")); >
The DELETE API is straightforward and down not return any response. It returns response code 204 when the entity is deleted.
Use delete() method to send HTTP DELETE requests.
@Test public void deleteUser_thenSuccess() < when() .delete("/users/2") .then() .statusCode(204) .body(isEmptyOrNullString()); >
4. Building Request Headers and Body
For sending request headers, use RequestSpecification.header() method.
given() . .header(new Header("x-custom-header", "value")) .contentType("application/json") .
For sending requests with a request body, we have two options:
- Either, using JSONObject to build a custom JSON payload. Internally, REST-assured uses JSON.simple to parse the object to JSON payload.
JSONObject requestParams = new JSONObject(); requestParams.put("name", "lokesh"); requestParams.put("email", "admin@howtodoinjava.com"); given() .body(requestParams.toString()) . .when()
given() .body(new User("lokesh", "admin@howtodoinjava.com")) . .when()
5. Verifying Response Status and Body
Use any of the Hamcrest matchers to match the parts of the response including response status code, response headers or response body.
given() . .when() . .then() .statusCode(201) .header("x-response-header", equalTo("value")) .body("id", notNullValue()) .body("name", equalTo("lokesh")) .body("email", equalTo("admin@howtodoinjava.com"))
6. Extracting Response data
Suppose we want to reuse the data from the response of an API, we can extract it using the extract() method.
String .body(requestParams.toString()) .contentType("application/json") .when() .post("/users") .then() .statusCode(equalTo(201)) .extract().body().path("id");
Use the ValidatableResponseLogSpec returned from body() method to log using the Hamcrest log() method.
given() . .when() . .then() . .log().body();
8. Measuring Response Time and Testing Timeouts
Using the time() method, we can measure the API response time for verifying performance SLAs. It returns the time in milliseconds. To get the time in a specific timeunit, pass the TimeUnit as method argument.
long timeInMs = get(". ").time(); long timeInSeconds = get(". ").timeIn(TimeUnit.SECONDS);
We can use the time() method with hamcrest matches to test the timeouts.
when(). get(". "). then(). time(lessThan(1000L), TimeUnit.SECONDS);
As a best practice, we should test the timeouts when JVM is warmed up. Measuring timeouts in a single test may not give the correct result as it will include a lot of other initializations at the JVM level.
In this tutorial, we learned using REST-assured to write automated tests for real API testing. This is an excellent solution for BDD development where we can start writing the API mocks and tests for it, and later we write the actual API implementations.
REST-assured Examples: GET, POST, PUT, PATCH, DELETE
This post explains how to send API HTTP Requests using REST-assured library. Examples cover GET , POST , PUT , PATCH and DELETE requests.
REST-assured HTTP API Requests
GET Request
The HTTP GET request is used to fetch a resource from a server.
The following example uses the get() method from REST-assured library.
import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.response.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; public class RestAssuredRequests < @BeforeAll public static void setup() < RestAssured.baseURI = "https://jsonplaceholder.typicode.com"; >@Test public void getRequest() < Response response = given() .contentType(ContentType.JSON) .when() .get("/posts") .then() .extract().response(); Assertions.assertEquals(200, response.statusCode()); Assertions.assertEquals("qui est esse", response.jsonPath().getString("title[1]")); >>
GET Request With Query Params
To send query parameters along with the GET request, we use the queryParam method:
import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.response.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; public class RestAssuredRequests < @BeforeAll public static void setup() < RestAssured.baseURI = "https://jsonplaceholder.typicode.com"; >@Test public void getRequestWithQueryParam() < Response response = given() .contentType(ContentType.JSON) .param("postId", "2") .when() .get("/comments") .then() .extract().response(); Assertions.assertEquals(200, response.statusCode()); Assertions.assertEquals("Meghan_Littel@rene.us", response.jsonPath().getString("email[3]")); >>
POST Request
HTTP POST request is used to post data or create a resource on a server.
To send a POST request in REST-assured, we use the post() method:
import io.restassured.RestAssured; import io.restassured.response.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; public class RestAssuredRequests < private static String requestBody = ""; @BeforeAll public static void setup() < RestAssured.baseURI = "https://jsonplaceholder.typicode.com"; >@Test public void postRequest() < Response response = given() .header("Content-type", "application/json") .and() .body(requestBody) .when() .post("/posts") .then() .extract().response(); Assertions.assertEquals(201, response.statusCode()); Assertions.assertEquals("foo", response.jsonPath().getString("title")); Assertions.assertEquals("bar", response.jsonPath().getString("body")); Assertions.assertEquals("1", response.jsonPath().getString("userId")); Assertions.assertEquals("101", response.jsonPath().getString("id")); >>
PUT Request
The PUT request updates a resource but requires the full JSON payload.
To send a PUT request in REST-assured, we use the put() method:
import io.restassured.RestAssured; import io.restassured.response.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; public class RestAssuredRequests < private static String requestBody = ""; @BeforeAll public static void setup() < RestAssured.baseURI = "https://jsonplaceholder.typicode.com"; >@Test public void putRequest() < Response response = given() .header("Content-type", "application/json") .and() .body(requestBody) .when() .put("/posts/1") .then() .extract().response(); Assertions.assertEquals(200, response.statusCode()); Assertions.assertEquals("foo", response.jsonPath().getString("title")); Assertions.assertEquals("baz", response.jsonPath().getString("body")); Assertions.assertEquals("1", response.jsonPath().getString("userId")); Assertions.assertEquals("1", response.jsonPath().getString("id")); >>
PATCH Request
The PATCH request updates a resource but requires only the field(s) which is being updated in the payload:
import io.restassured.RestAssured; import io.restassured.response.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; public class RestAssuredRequests < private static String requestBody = ""; @BeforeAll public static void setup() < RestAssured.baseURI = "https://jsonplaceholder.typicode.com"; >@Test public void patchRequest() < Response response = given() .header("Content-type", "application/json") .and() .body(requestBody) .when() .patch("/posts/1") .then() .extract().response(); Assertions.assertEquals(200, response.statusCode()); Assertions.assertEquals("bax", response.jsonPath().getString("title")); Assertions.assertEquals("1", response.jsonPath().getString("userId")); Assertions.assertEquals("1", response.jsonPath().getString("id")); >>
DELETE Request
The DELETE request is used to delete a resource from a server.
To send a DELETE request in REST-assured, we use the delete() method:
import io.restassured.RestAssured; import io.restassured.response.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; public class RestAssuredRequests < @BeforeAll public static void setup() < RestAssured.baseURI = "https://jsonplaceholder.typicode.com"; >@Test public void deleteRequest() < Response response = given() .header("Content-type", "application/json") .when() .delete("/posts/1") .then() .extract().response(); Assertions.assertEquals(200, response.statusCode()); >>