WebTestClient предоставляет API-интерфейс, идентичный WebClient, вплоть до выполнения запроса с помощью exchange(). Примеры подготовки запроса с любым содержимым, включая данные формы, многокомпонентные данные и многое другое, см. в документации по WebClient.

После вызова exchange() WebTestClient расходится с WebClient и вместо этого продолжает рабочий процесс проверки достоверности ответов.

Чтобы подтвердить статус ответа и заголовки, используйте следующее:

Java
client.get().uri("/persons/1")
    .accept(MediaType.APPLICATION_JSON)
    .exchange()
    .expectStatus().isOk()
    .expectHeader().contentType(MediaType.APPLICATION_JSON);
Kotlin
client.get().uri("/persons/1")
    .accept(MediaType.APPLICATION_JSON)
    .exchange()
    .expectStatus().isOk()
    .expectHeader().contentType(MediaType.APPLICATION_JSON)

Если нужно, чтобы все ожидаемые события были подтверждены, даже если одно из них не сработало, то можно использовать expectAll(..) вместо нескольких последовательных вызовов expect*(..). Эта функция похожа на поддержку мягких утверждений в AssertJ и assertAll() в JUnit Jupiter.

Java
client.get().uri("/persons/1")
    .accept(MediaType.APPLICATION_JSON)
    .exchange()
    .expectAll(
        spec -> spec.expectStatus().isOk(),
        spec -> spec.expectHeader().contentType(MediaType.APPLICATION_JSON)
    );

Затем можно выбрать декодирование тела ответа с помощью одного из следующих способов:

  • expectBody(Class<T>): Декодируем в один объект.

  • expectBodyList(Class<T>): Декодируемым и собираем объекты в List<T>.

  • expectBody(): Декодируем в byte[] для содержимого JSON или пустого тела.

И выполняем утверждения на результирующем объекте(ах) более высокого уровня:

Java
client.get().uri("/persons")
        .exchange()
        .expectStatus().isOk()
        .expectBodyList(Person.class).hasSize(3).contains(person);
Kotlin
import org.springframework.test.web.reactive.server.expectBodyList
client.get().uri("/persons")
        .exchange()
        .expectStatus().isOk()
        .expectBodyList<Person>().hasSize(3).contains(person)

Если встроенных утверждений недостаточно, можно использовать вместо этого объект и выполнить любые другие утверждения:

Java
import org.springframework.test.web.reactive.server.expectBody
client.get().uri("/persons/1")
        .exchange()
        .expectStatus().isOk()
        .expectBody(Person.class)
        .consumeWith(result -> {
            // специальные утверждения (например, AssertJ)...
        });
Kotlin
client.get().uri("/persons/1")
        .exchange()
        .expectStatus().isOk()
        .expectBody<Person>()
        .consumeWith {
            // специальные утверждения (например, AssertJ)...
        }

Или вы можете выйти из рабочего процесса и получить EntityExchangeResult:

Java
EntityExchangeResult<Person> result = client.get().uri("/persons/1")
        .exchange()
        .expectStatus().isOk()
        .expectBody(Person.class)
        .returnResult();
Kotlin
import org.springframework.test.web.reactive.server.expectBody
val result = client.get().uri("/persons/1")
        .exchange()
        .expectStatus().isOk
        .expectBody<Person>()
        .returnResult()
Если нужно декодировать в целевой тип с помощью обобщений, ищите перегруженные методы, которые принимают ParameterizedTypeReference вместо Class<T>.

Отсутствие содержимого

Если ожидается, что в ответе не будет содержимого, то вы можете подтвердить это следующим образом:

Java
client.post().uri("/persons")
        .body(personMono, Person.class)
        .exchange()
        .expectStatus().isCreated()
        .expectBody().isEmpty();
Kotlin
client.post().uri("/persons")
        .bodyValue(person)
        .exchange()
        .expectStatus().isCreated()
        .expectBody().isEmpty()

Если нужно проигнорировать содержимое ответа, то ниже приведено высвобождение содержимого без каких-либо утверждений:

Java
client.get().uri("/persons/123")
        .exchange()
        .expectStatus().isNotFound()
        .expectBody(Void.class);
Kotlin
client.get().uri("/persons/123")
        .exchange()
        .expectStatus().isNotFound
        .expectBody<Unit>()

Содержимое JSON

Можно использовать expectBody() без целевого типа, чтобы выполнять утверждения для неформатированного содержимого, а не делать это через объект(ы) более высокого уровня.

Чтобы проверить полное содержимое JSON с помощью JSONAssert:

Java
client.get().uri("/persons/1")
        .exchange()
        .expectStatus().isOk()
        .expectBody()
        .json("{\"name\":\"Jane\"}")
Kotlin
client.get().uri("/persons/1")
        .exchange()
        .expectStatus().isOk()
        .expectBody()
        .json("{\"name\":\"Jane\"}")

Чтобы проверить содержимое JSON с помощью JSONPath:

Java
client.get().uri("/persons")
        .exchange()
        .expectStatus().isOk()
        .expectBody()
        .jsonPath("$[0].name").isEqualTo("Jane")
        .jsonPath("$[1].name").isEqualTo("Jason");
Kotlin
client.get().uri("/persons")
        .exchange()
        .expectStatus().isOk()
        .expectBody()
        .jsonPath("$[0].name").isEqualTo("Jane")
        .jsonPath("$[1].name").isEqualTo("Jason")

Потоковые ответы

Чтобы проверить потенциально бесконечные потоки, такие как "text/event-stream" или "application/x-ndjson", начните с проверки статуса и заголовков ответа, а затем получите FluxExchangeResult:

Java
FluxExchangeResult<MyEvent> result = client.get().uri("/events")
        .accept(TEXT_EVENT_STREAM)
        .exchange()
        .expectStatus().isOk()
        .returnResult(MyEvent.class);
Kotlin
import org.springframework.test.web.reactive.server.returnResult
val result = client.get().uri("/events")
        .accept(TEXT_EVENT_STREAM)
        .exchange()
        .expectStatus().isOk()
        .returnResult<MyEvent>()

Теперь вы готовы потреблять поток ответов с помощью StepVerifier из reactor-test:

Java
Flux<Event> eventFlux = result.getResponseBody();
StepVerifier.create(eventFlux)
        .expectNext(person)
        .expectNextCount(4)
        .consumeNextWith(p -> ...)
        .thenCancel()
        .verify();
Kotlin
val eventFlux = result.getResponseBody()
StepVerifier.create(eventFlux)
        .expectNext(person)
        .expectNextCount(4)
        .consumeNextWith { p -> ... }
        .thenCancel()
        .verify()

Утверждения MockMvc

WebTestClient – это HTTP-клиент, поэтому он может проверять только то, что находится в ответе клиента, включая статус, заголовки и тело.

При тестировании приложения Spring MVC с настройкой сервера MockMvc вам доступна дополнительная возможность выполнять дальнейшие утверждения для ответа сервера. Для этого начните с получения ExchangeResult после добавления утверждения к телу:

Java
// Для ответа с телом
EntityExchangeResult<Person> result = client.get().uri("/persons/1")
        .exchange()
        .expectStatus().isOk()
        .expectBody(Person.class)
        .returnResult();
// Для ответа без тела
EntityExchangeResult<Void> result = client.get().uri("/path")
        .exchange()
        .expectBody().isEmpty();
Kotlin
// Для ответа с телом
val result = client.get().uri("/persons/1")
        .exchange()
        .expectStatus().isOk()
        .expectBody(Person.class)
        .returnResult();
// Для ответа без тела
val result = client.get().uri("/path")
        .exchange()
        .expectBody().isEmpty();

Затем переключитесь на утверждения ответа сервера MockMvc:

Java
MockMvcWebTestClient.resultActionsFor(result)
        .andExpect(model().attribute("integer", 3))
        .andExpect(model().attribute("string", "a string value"));
Kotlin
MockMvcWebTestClient.resultActionsFor(result)
        .andExpect(model().attribute("integer", 3))
        .andExpect(model().attribute("string", "a string value"));