5

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?

1

3 Answers 3

6

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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".
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.
For anyone wondering how to get the raw value out of the snippet above, the below should give you the raw response string ➡️ var rawJson = new String(result.getResponseBody())
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();

Comments

1

Use value(Consumer<T> consumer) on .jsonPath("<path>") result and "consume" the value of the JSON field into a variable.

Kotlin code below:

var testId = ""
webClient.post()
  .uri("/api/test")
  .contentType(MediaType.APPLICATION_JSON)
  .body(BodyInserters.fromValue(testPostBodyString))
  .accept(MediaType.APPLICATION_JSON)
  .exchange()
  .expectStatus().is2xxSuccessful
  .expectBody()
  .jsonPath("$.testId").isNotEmpty()
  .jsonPath("$.testId").value<String> { testId = it}

1 Comment

Nice! For Java programmers, use something like this java final AtomicReference<String> testId = new AtomicReference<>(); //... .jsonPath("$.testId").value(testId::set);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.