595 lines
16 KiB
Plaintext
595 lines
16 KiB
Plaintext
|
[[webtestclient]]
|
||
|
= WebTestClient
|
||
|
|
||
|
`WebTestClient` is an HTTP client designed for testing server applications. It wraps
|
||
|
Spring's <<web-reactive.adoc#webflux-client, WebClient>> and uses it to perform requests
|
||
|
but exposes a testing facade for verifying responses. `WebTestClient` can be used to
|
||
|
perform end-to-end HTTP tests. It can also be used to test Spring MVC and Spring WebFlux
|
||
|
applications without a running server via mock server request and response objects.
|
||
|
|
||
|
TIP: Kotlin users: See <<languages.adoc#kotlin-webtestclient-issue, this section>>
|
||
|
related to use of the `WebTestClient`.
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
[[webtestclient-setup]]
|
||
|
== Setup
|
||
|
|
||
|
To set up a `WebTestClient` you need to choose a server setup to bind to. This can be one
|
||
|
of several mock server setup choices or a connection to a live server.
|
||
|
|
||
|
|
||
|
|
||
|
[[webtestclient-controller-config]]
|
||
|
=== Bind to Controller
|
||
|
|
||
|
This setup allows you to test specific controller(s) via mock request and response objects,
|
||
|
without a running server.
|
||
|
|
||
|
For WebFlux applications, use the following which loads infrastructure equivalent to the
|
||
|
<<web-reactive.adoc#webflux-config, WebFlux Java config>>, registers the given
|
||
|
controller(s), and creates a <<web-reactive.adoc#webflux-web-handler-api, WebHandler chain>>
|
||
|
to handle requests:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
WebTestClient client =
|
||
|
WebTestClient.bindToController(new TestController()).build();
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
val client = WebTestClient.bindToController(TestController()).build()
|
||
|
----
|
||
|
|
||
|
For Spring MVC, use the following which delegates to the
|
||
|
{api-spring-framework}/test/web/servlet/setup/StandaloneMockMvcBuilder.html[StandaloneMockMvcBuilder]
|
||
|
to load infrastructure equivalent to the <<web.adoc#mvc-config, WebMvc Java config>>,
|
||
|
registers the given controller(s), and creates an instance of
|
||
|
<<testing.adoc#spring-mvc-test-framework, MockMvc>> to handle requests:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
WebTestClient client =
|
||
|
MockMvcWebTestClient.bindToController(new TestController()).build();
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
val client = MockMvcWebTestClient.bindToController(TestController()).build()
|
||
|
----
|
||
|
|
||
|
|
||
|
|
||
|
[[webtestclient-context-config]]
|
||
|
=== Bind to `ApplicationContext`
|
||
|
|
||
|
This setup allows you to load Spring configuration with Spring MVC or Spring WebFlux
|
||
|
infrastructure and controller declarations and use it to handle requests via mock request
|
||
|
and response objects, without a running server.
|
||
|
|
||
|
For WebFlux, use the following where the Spring `ApplicationContext` is passed to
|
||
|
{api-spring-framework}/web/server/adapter/WebHttpHandlerBuilder.html#applicationContext-org.springframework.context.ApplicationContext-[WebHttpHandlerBuilder]
|
||
|
to create the <<web-reactive.adoc#webflux-web-handler-api, WebHandler chain>> to handle
|
||
|
requests:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
@SpringJUnitConfig(WebConfig.class) // <1>
|
||
|
class MyTests {
|
||
|
|
||
|
WebTestClient client;
|
||
|
|
||
|
@BeforeEach
|
||
|
void setUp(ApplicationContext context) { // <2>
|
||
|
client = WebTestClient.bindToApplicationContext(context).build(); // <3>
|
||
|
}
|
||
|
}
|
||
|
----
|
||
|
<1> Specify the configuration to load
|
||
|
<2> Inject the configuration
|
||
|
<3> Create the `WebTestClient`
|
||
|
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
@SpringJUnitConfig(WebConfig::class) // <1>
|
||
|
class MyTests {
|
||
|
|
||
|
lateinit var client: WebTestClient
|
||
|
|
||
|
@BeforeEach
|
||
|
fun setUp(context: ApplicationContext) { // <2>
|
||
|
client = WebTestClient.bindToApplicationContext(context).build() // <3>
|
||
|
}
|
||
|
}
|
||
|
----
|
||
|
<1> Specify the configuration to load
|
||
|
<2> Inject the configuration
|
||
|
<3> Create the `WebTestClient`
|
||
|
|
||
|
For Spring MVC, use the following where the Spring `ApplicationContext` is passed to
|
||
|
{api-spring-framework}/test/web/servlet/setup/MockMvcBuilders.html#webAppContextSetup-org.springframework.web.context.WebApplicationContext-[MockMvcBuilders.webAppContextSetup]
|
||
|
to create a <<testing.adoc#spring-mvc-test-framework, MockMvc>> instance to handle
|
||
|
requests:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
@ExtendWith(SpringExtension.class)
|
||
|
@WebAppConfiguration("classpath:META-INF/web-resources") // <1>
|
||
|
@ContextHierarchy({
|
||
|
@ContextConfiguration(classes = RootConfig.class),
|
||
|
@ContextConfiguration(classes = WebConfig.class)
|
||
|
})
|
||
|
class MyTests {
|
||
|
|
||
|
@Autowired
|
||
|
WebApplicationContext wac; // <2>
|
||
|
|
||
|
WebTestClient client;
|
||
|
|
||
|
@BeforeEach
|
||
|
void setUp() {
|
||
|
client = MockMvcWebTestClient.bindToApplicationContext(this.wac).build(); // <3>
|
||
|
}
|
||
|
}
|
||
|
----
|
||
|
<1> Specify the configuration to load
|
||
|
<2> Inject the configuration
|
||
|
<3> Create the `WebTestClient`
|
||
|
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
@ExtendWith(SpringExtension.class)
|
||
|
@WebAppConfiguration("classpath:META-INF/web-resources") // <1>
|
||
|
@ContextHierarchy({
|
||
|
@ContextConfiguration(classes = RootConfig.class),
|
||
|
@ContextConfiguration(classes = WebConfig.class)
|
||
|
})
|
||
|
class MyTests {
|
||
|
|
||
|
@Autowired
|
||
|
lateinit var wac: WebApplicationContext; // <2>
|
||
|
|
||
|
lateinit var client: WebTestClient
|
||
|
|
||
|
@BeforeEach
|
||
|
fun setUp() { // <2>
|
||
|
client = MockMvcWebTestClient.bindToApplicationContext(wac).build() // <3>
|
||
|
}
|
||
|
}
|
||
|
----
|
||
|
<1> Specify the configuration to load
|
||
|
<2> Inject the configuration
|
||
|
<3> Create the `WebTestClient`
|
||
|
|
||
|
|
||
|
|
||
|
[[webtestclient-fn-config]]
|
||
|
=== Bind to Router Function
|
||
|
|
||
|
This setup allows you to test <<web-reactive.adoc#webflux-fn, functional endpoints>> via
|
||
|
mock request and response objects, without a running server.
|
||
|
|
||
|
For WebFlux, use the following which delegates to `RouterFunctions.toWebHandler` to
|
||
|
create a server setup to handle requests:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
RouterFunction<?> route = ...
|
||
|
client = WebTestClient.bindToRouterFunction(route).build();
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
val route: RouterFunction<*> = ...
|
||
|
val client = WebTestClient.bindToRouterFunction(route).build()
|
||
|
----
|
||
|
|
||
|
For Spring MVC there are currently no options to test
|
||
|
<<web.adoc#webmvc-fn, WebMvc functional endpoints>>.
|
||
|
|
||
|
|
||
|
|
||
|
[[webtestclient-server-config]]
|
||
|
=== Bind to Server
|
||
|
|
||
|
This setup connects to a running server to perform full, end-to-end HTTP tests:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
client = WebTestClient.bindToServer().baseUrl("http://localhost:8080").build();
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
client = WebTestClient.bindToServer().baseUrl("http://localhost:8080").build()
|
||
|
----
|
||
|
|
||
|
|
||
|
|
||
|
[[webtestclient-client-config]]
|
||
|
=== Client Config
|
||
|
|
||
|
In addition to the server setup options described earlier, you can also configure client
|
||
|
options, including base URL, default headers, client filters, and others. These options
|
||
|
are readily available following `bindToServer()`. For all other configuration options,
|
||
|
you need to use `configureClient()` to transition from server to client configuration, as
|
||
|
follows:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
client = WebTestClient.bindToController(new TestController())
|
||
|
.configureClient()
|
||
|
.baseUrl("/test")
|
||
|
.build();
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
client = WebTestClient.bindToController(TestController())
|
||
|
.configureClient()
|
||
|
.baseUrl("/test")
|
||
|
.build()
|
||
|
----
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
[[webtestclient-tests]]
|
||
|
== Writing Tests
|
||
|
|
||
|
`WebTestClient` provides an API identical to <<web-reactive.adoc#webflux-client, WebClient>>
|
||
|
up to the point of performing a request by using `exchange()`. See the
|
||
|
<<web-reactive.adoc#webflux-client-body, WebClient>> documentation for examples on how to
|
||
|
prepare a request with any content including form data, multipart data, and more.
|
||
|
|
||
|
After the call to `exchange()`, `WebTestClient` diverges from the `WebClient` and
|
||
|
instead continues with a workflow to verify responses.
|
||
|
|
||
|
To assert the response status and headers, use the following:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
client.get().uri("/persons/1")
|
||
|
.accept(MediaType.APPLICATION_JSON)
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectHeader().contentType(MediaType.APPLICATION_JSON);
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
client.get().uri("/persons/1")
|
||
|
.accept(MediaType.APPLICATION_JSON)
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectHeader().contentType(MediaType.APPLICATION_JSON)
|
||
|
----
|
||
|
|
||
|
If you would like for all expectations to be asserted even if one of them fails, you can
|
||
|
use `expectAll(..)` instead of multiple chained `expect*(..)` calls. This feature is
|
||
|
similar to the _soft assertions_ support in AssertJ and the `assertAll()` support in
|
||
|
JUnit Jupiter.
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
client.get().uri("/persons/1")
|
||
|
.accept(MediaType.APPLICATION_JSON)
|
||
|
.exchange()
|
||
|
.expectAll(
|
||
|
spec -> spec.expectStatus().isOk(),
|
||
|
spec -> spec.expectHeader().contentType(MediaType.APPLICATION_JSON)
|
||
|
);
|
||
|
----
|
||
|
|
||
|
You can then choose to decode the response body through one of the following:
|
||
|
|
||
|
* `expectBody(Class<T>)`: Decode to single object.
|
||
|
* `expectBodyList(Class<T>)`: Decode and collect objects to `List<T>`.
|
||
|
* `expectBody()`: Decode to `byte[]` for <<webtestclient-json>> or an empty body.
|
||
|
|
||
|
And perform assertions on the resulting higher level Object(s):
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
client.get().uri("/persons")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBodyList(Person.class).hasSize(3).contains(person);
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
import org.springframework.test.web.reactive.server.expectBodyList
|
||
|
|
||
|
client.get().uri("/persons")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBodyList<Person>().hasSize(3).contains(person)
|
||
|
----
|
||
|
|
||
|
If the built-in assertions are insufficient, you can consume the object instead and
|
||
|
perform any other assertions:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
import org.springframework.test.web.reactive.server.expectBody
|
||
|
|
||
|
client.get().uri("/persons/1")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBody(Person.class)
|
||
|
.consumeWith(result -> {
|
||
|
// custom assertions (e.g. AssertJ)...
|
||
|
});
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
client.get().uri("/persons/1")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBody<Person>()
|
||
|
.consumeWith {
|
||
|
// custom assertions (e.g. AssertJ)...
|
||
|
}
|
||
|
----
|
||
|
|
||
|
Or you can exit the workflow and obtain an `EntityExchangeResult`:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
EntityExchangeResult<Person> result = client.get().uri("/persons/1")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBody(Person.class)
|
||
|
.returnResult();
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
import org.springframework.test.web.reactive.server.expectBody
|
||
|
|
||
|
val result = client.get().uri("/persons/1")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk
|
||
|
.expectBody<Person>()
|
||
|
.returnResult()
|
||
|
----
|
||
|
|
||
|
TIP: When you need to decode to a target type with generics, look for the overloaded methods
|
||
|
that accept
|
||
|
{api-spring-framework}/core/ParameterizedTypeReference.html[`ParameterizedTypeReference`]
|
||
|
instead of `Class<T>`.
|
||
|
|
||
|
|
||
|
|
||
|
[[webtestclient-no-content]]
|
||
|
=== No Content
|
||
|
|
||
|
If the response is not expected to have content, you can assert that as follows:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
client.post().uri("/persons")
|
||
|
.body(personMono, Person.class)
|
||
|
.exchange()
|
||
|
.expectStatus().isCreated()
|
||
|
.expectBody().isEmpty();
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
client.post().uri("/persons")
|
||
|
.bodyValue(person)
|
||
|
.exchange()
|
||
|
.expectStatus().isCreated()
|
||
|
.expectBody().isEmpty()
|
||
|
----
|
||
|
|
||
|
If you want to ignore the response content, the following releases the content without
|
||
|
any assertions:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
client.get().uri("/persons/123")
|
||
|
.exchange()
|
||
|
.expectStatus().isNotFound()
|
||
|
.expectBody(Void.class);
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
client.get().uri("/persons/123")
|
||
|
.exchange()
|
||
|
.expectStatus().isNotFound
|
||
|
.expectBody<Unit>()
|
||
|
----
|
||
|
|
||
|
|
||
|
|
||
|
[[webtestclient-json]]
|
||
|
=== JSON Content
|
||
|
|
||
|
You can use `expectBody()` without a target type to perform assertions on the raw
|
||
|
content rather than through higher level Object(s).
|
||
|
|
||
|
To verify the full JSON content with https://jsonassert.skyscreamer.org[JSONAssert]:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
client.get().uri("/persons/1")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBody()
|
||
|
.json("{\"name\":\"Jane\"}")
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
client.get().uri("/persons/1")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBody()
|
||
|
.json("{\"name\":\"Jane\"}")
|
||
|
----
|
||
|
|
||
|
To verify JSON content with https://github.com/jayway/JsonPath[JSONPath]:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
client.get().uri("/persons")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBody()
|
||
|
.jsonPath("$[0].name").isEqualTo("Jane")
|
||
|
.jsonPath("$[1].name").isEqualTo("Jason");
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
client.get().uri("/persons")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBody()
|
||
|
.jsonPath("$[0].name").isEqualTo("Jane")
|
||
|
.jsonPath("$[1].name").isEqualTo("Jason")
|
||
|
----
|
||
|
|
||
|
|
||
|
|
||
|
[[webtestclient-stream]]
|
||
|
=== Streaming Responses
|
||
|
|
||
|
To test potentially infinite streams such as `"text/event-stream"` or
|
||
|
`"application/x-ndjson"`, start by verifying the response status and headers, and then
|
||
|
obtain a `FluxExchangeResult`:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
FluxExchangeResult<MyEvent> result = client.get().uri("/events")
|
||
|
.accept(TEXT_EVENT_STREAM)
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.returnResult(MyEvent.class);
|
||
|
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
import org.springframework.test.web.reactive.server.returnResult
|
||
|
|
||
|
val result = client.get().uri("/events")
|
||
|
.accept(TEXT_EVENT_STREAM)
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.returnResult<MyEvent>()
|
||
|
----
|
||
|
|
||
|
Now you're ready to consume the response stream with `StepVerifier` from `reactor-test`:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
Flux<Event> eventFlux = result.getResponseBody();
|
||
|
|
||
|
StepVerifier.create(eventFlux)
|
||
|
.expectNext(person)
|
||
|
.expectNextCount(4)
|
||
|
.consumeNextWith(p -> ...)
|
||
|
.thenCancel()
|
||
|
.verify();
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
val eventFlux = result.getResponseBody()
|
||
|
|
||
|
StepVerifier.create(eventFlux)
|
||
|
.expectNext(person)
|
||
|
.expectNextCount(4)
|
||
|
.consumeNextWith { p -> ... }
|
||
|
.thenCancel()
|
||
|
.verify()
|
||
|
----
|
||
|
|
||
|
|
||
|
[[webtestclient-mockmvc]]
|
||
|
=== MockMvc Assertions
|
||
|
|
||
|
`WebTestClient` is an HTTP client and as such it can only verify what is in the client
|
||
|
response including status, headers, and body.
|
||
|
|
||
|
When testing a Spring MVC application with a MockMvc server setup, you have the extra
|
||
|
choice to perform further assertions on the server response. To do that start by
|
||
|
obtaining an `ExchangeResult` after asserting the body:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
// For a response with a body
|
||
|
EntityExchangeResult<Person> result = client.get().uri("/persons/1")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBody(Person.class)
|
||
|
.returnResult();
|
||
|
|
||
|
// For a response without a body
|
||
|
EntityExchangeResult<Void> result = client.get().uri("/path")
|
||
|
.exchange()
|
||
|
.expectBody().isEmpty();
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
// For a response with a body
|
||
|
val result = client.get().uri("/persons/1")
|
||
|
.exchange()
|
||
|
.expectStatus().isOk()
|
||
|
.expectBody(Person.class)
|
||
|
.returnResult();
|
||
|
|
||
|
// For a response without a body
|
||
|
val result = client.get().uri("/path")
|
||
|
.exchange()
|
||
|
.expectBody().isEmpty();
|
||
|
----
|
||
|
|
||
|
Then switch to MockMvc server response assertions:
|
||
|
|
||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||
|
.Java
|
||
|
----
|
||
|
MockMvcWebTestClient.resultActionsFor(result)
|
||
|
.andExpect(model().attribute("integer", 3))
|
||
|
.andExpect(model().attribute("string", "a string value"));
|
||
|
----
|
||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||
|
.Kotlin
|
||
|
----
|
||
|
MockMvcWebTestClient.resultActionsFor(result)
|
||
|
.andExpect(model().attribute("integer", 3))
|
||
|
.andExpect(model().attribute("string", "a string value"));
|
||
|
----
|
||
|
|