Spring Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

REST Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we’ll discuss how to test REST services using REST-assured, with a focus on capturing and validating the response data from our REST APIs.

2. Setup for the Test Class

In previous tutorials, we’ve explored REST-assured in general, and we’ve shown how to manipulate request headers, cookies and parameters.

Building on this existing setup, we’ve added a simple REST controller, AppController, that internally calls a service, AppService. We’ll use these classes in our test examples.

To create our test class, we need to do a bit more setup. Since we have spring-boot-starter-test in our classpath, we can easily leverage Spring testing utilities.

First, let’s create the skeleton of our AppControllerIntegrationTest class:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class AppControllerIntegrationTest {

    @LocalServerPort
    private int port;

    private String uri;

    @PostConstruct
    public void init() {
        uri = "http://localhost:" + port;
    }

    @MockBean
    AppService appService;

     //test cases
}

In this JUnit test, we annotated our class with a couple of Spring-specific annotations that spin up the application locally in a random available port. In @PostConstruct, we captured the full URI on which we will be making REST calls.

We also used @MockBean on AppService, as we need to mock method calls on this class.

3. Validating the JSON Response

JSON is the most common format used in REST APIs to exchange data. Responses can consist of a single JSON object or an array of JSON objects. We’ll look at both in this section.

3.1. Single JSON Object

Let’s say we need to test the /movie/{id} endpoint, which returns a Movie JSON object if the id is found.

We’ll mock AppService calls to return some mock data using the Mockito framework:

@Test
public void givenMovieId_whenMakingGetRequestToMovieEndpoint_thenReturnMovie() {

    Movie testMovie = new Movie(1, "movie1", "summary1");
    when(appService.findMovie(1)).thenReturn(testMovie);

    get(uri + "/movie/" + testMovie.getId()).then()
      .assertThat()
      .statusCode(HttpStatus.OK.value())
      .body("id", equalTo(testMovie.getId()))
      .body("name", equalTo(testMovie.getName()))
      .body("synopsis", notNullValue());
}

Above, we first mocked the appService.findMovie(1) call to return an object. Then, we constructed our REST URL in the get() method provided by REST-assured for making GET requests. Finally, we made four assertions.

First, we checked the response status code and then the body elements. We’re using Hamcrest to assert the expected value.

Also note that if the response JSON is nested, we can test a nested key by using the dot operator like “key1.key2.key3”.

3.2. Extracting the JSON Response After Validation

In some cases, we may need to extract the response after validation, to perform additional operations on it.

We can extract the JSON response to a class, using the extract() method:

Movie result = get(uri + "/movie/" + testMovie.getId()).then()
  .assertThat()
  .statusCode(HttpStatus.OK.value())
  .extract()
  .as(Movie.class);
assertThat(result).isEqualTo(testMovie);

In this example, we directed REST-assured to extract the JSON response to a Movie object and then asserted on the extracted object.

We can also extract the whole response to a String, using the extract().asString() API:

String responseString = get(uri + "/movie/" + testMovie.getId()).then()
  .assertThat()
  .statusCode(HttpStatus.OK.value())
  .extract()
  .asString();
assertThat(responseString).isNotEmpty();

Finally, we can extract a particular field out of the response JSON as well.

Let’s look at a test for a POST API that expects a Movie JSON body and will return the same if inserted successfully:

@Test
public void givenMovie_whenMakingPostRequestToMovieEndpoint_thenCorrect() {
    Map<String, String> request = new HashMap<>();
    request.put("id", "11");
    request.put("name", "movie1");
    request.put("synopsis", "summary1");

    int movieId = given().contentType("application/json")
      .body(request)
      .when()
      .post(uri + "/movie")
      .then()
      .assertThat()
      .statusCode(HttpStatus.CREATED.value())
      .extract()
      .path("id");
    assertThat(movieId).isEqualTo(11);
}

Above, we first made the request object that we need to POST. We then extracted the id field from the returned JSON response using the path() method.

3.3. JSON Array

We can also verify the response if it’s a JSON array:

@Test
public void whenCallingMoviesEndpoint_thenReturnAllMovies() {

Set<Movie> movieSet = new HashSet<>();
movieSet.add(new Movie(1, "movie1", "summary1"));
movieSet.add(new Movie(2, "movie2", "summary2"));
when(appService.getAll()).thenReturn(movieSet);

get(uri + "/movies").then()
    .statusCode(HttpStatus.OK.value())
    .assertThat()
    .body("size()", is(2));
}

We again first mocked the appService.getAll() with some data and made a request to our endpoint. We then asserted the statusCode and size of our response array.

This again can be done via extraction:

Movie[] movies = get(uri + "/movies").then()
  .statusCode(200)
  .extract()
  .as(Movie[].class);
assertThat(movies.length).isEqualTo(2);

We can verify a header or cookie of the response using methods with the same name:

@Test
public void whenCallingWelcomeEndpoint_thenCorrect() {
    get(uri + "/welcome").then()
        .assertThat()
        .header("sessionId", notNullValue())
        .cookie("token", notNullValue());
}

We can also extract the headers and cookies individually:

Response response = get(uri + "/welcome");

String headerName = response.getHeader("sessionId");
String cookieValue = response.getCookie("token");
assertThat(headerName).isNotBlank();
assertThat(cookieValue).isNotBlank();

5. Validating Files

If our REST API returns a file, we can use the asByteArray() method to extract the response:

File file = new ClassPathResource("test.txt").getFile();
long fileSize = file.length();
when(appService.getFile(1)).thenReturn(file);

byte[] result = get(uri + "/download/1").asByteArray();

assertThat(result.length).isEqualTo(fileSize);

Here, we first mocked appService.getFile(1) to return a text file that is present in our src/test/resources path. We then made a call to our endpoint and extracted the response in a byte[], which we then asserted to have the expected value.

6. Conclusion

In this tutorial, we looked into different ways of capturing and validating responses from our REST APIs using REST-assured.

As usual, the code in this article is available over on Github.

Spring bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

REST bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE