3

With Spring Boot 2.4.2 I'm using the WebTestClient to invoke requests in my integration tests.

This is the first request which gets a list of messages:

webTestClient
    .get()
    .uri("/api/messages")
    .headers(http -> http.setBearerAuth(token))
    .exchange()
    .expectStatus().isOk()
    .expectHeader().contentType(APPLICATION_JSON)
    .expectBody()
    .jsonPath("$.length()").isEqualTo(1)
    .jsonPath("$[0].id").isNumber()
    .jsonPath("$[0].type").isEqualTo(4);

Now I'd like to invoke a subsequent request to download a specific message. For this, I need the id which was already checked with jsonPath("$[0].id").

webTestClient
    .get()
    .uri(uriBuilder -> uriBuilder.path("/api/messages/{id}").build(extractedId))
    .headers(http -> http.setBearerAuth(token))
    .exchange()
    .expectStatus().isOk();

How can I extract this id into a local variable or else, so that it's available for the second request?

Robert Strauch
  • 12,055
  • 24
  • 120
  • 192
  • I went through this post some time ago, maybe it could be of help to you. https://www.blazemeter.com/blog/how-to-extract-values-when-api-testing-with-rest-assured – Eteka Christopher Jun 18 '21 at 09:50

2 Answers2

4

You can check their official docs for it.

But expanding the answer a bit, easiest way would be this

val result = webTestClient
                .get()
                .uri(uriBuilder -> uriBuilder.path("/api/messages/{id}").build(extractedId))
                .headers(http -> http.setBearerAuth(token))
                .exchange()
                .expectStatus().isOk()
                .returnResult();

There are also ways to get an (infinite) stream of responses explained on docs, which is pretty similar to the example above.

Lucas Campos
  • 1,860
  • 11
  • 17
  • As far as I see, this returns the complete response and would require converting the response body from a byte array to some kind of processable object for then extracting the parameter. I thought more of something like "Hey, just extract this single JSON value". – Robert Strauch Jun 18 '21 at 10:03
  • 1
    I am not sure how you would do that... if you are only interested in the `id`, you could create a simple POJO with only the `id` field and use that on the `expectBody` which will tell it to convert the response to that field automatically. Just make sure you don't fail on unknown properties. – Lucas Campos Jun 18 '21 at 10:33
2

I encountered the same problem, and this is the solution I came up with.

According to documentation,

WebTestClient is a thin shell around WebClient, using it to perform requests and exposing a dedicated, fluent API for verifying responses.

To my understanding it is geared for testing the response in a similar fashion as assertThat assertations do.

In my solution I instead use WebClient for extracting values.

The code snippet below should explain all the details. Note that it is just a generalized example, you should tailor it for your needs.

import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;

import static org.springframework.http.MediaType.APPLICATION_JSON;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class FooTest {

    @Autowired
    private WebTestClient webTestClient;

    /**
     * The port of the server. It starts on a RANDOM_PORT. @LocalServerPort is a way to find out what this port is.
     */
    @LocalServerPort
    private int port;

    @Test
    void someTestMethod() throws JSONException {


        // Create the request body that we'll send with the POST request.
        String postRequestBody = new JSONObject()
                .put("JsonField_1", "value a")
                .put("JsonFiled_2", "value b")
                // .put("any_additional_json_fields", "with_any_values")
                .toString();

        // The URI where we'll send the request to.
        String postRequestUri = "http://localhost:" + String.valueOf(port) + "/some_api";

        // Send a POST request, and save the response.
        TypeOfResponseWeExpect response = WebClient.create()
                .post()
                .uri(postRequestUri)
                .contentType(APPLICATION_JSON)
                .accept(APPLICATION_JSON)
                .body(BodyInserters.fromValue(postRequestBody))
                .retrieve()
                .bodyToMono(TypeOfResponseWeExpect.class)
                .block();

        // And now we can extract any values from the response.
        long extractedId = response.getId();
        String token = response.getToken();
        FooField fooField = response.getFoo();
        BarField barField = response.getBar();

        // Now we can use the extracted id field, or any field from the response.
        webTestClient
                .get()
                .uri(uriBuilder -> uriBuilder.path("/api/messages/{id}").build(extractedId))
                .headers(http -> http.setBearerAuth(token))
                .exchange()
                .expectStatus().isOk();
    }
}

Edit: After some further tries, I found a way to extract the response with WebTestClient too:

TypeOfResponseWeExpect response = this.webTestClient
        .post()
        .uri(postRequestUri)
        .contentType(APPLICATION_JSON)
        .accept(APPLICATION_JSON)
        .body(BodyInserters.fromValue(postRequestBody))
        .exchange()
        .expectBody(TypeOfResponseWeExpect.class)
        .returnResult()
        .getResponseBody();
Tamas K
  • 96
  • 4