More updates to Web testing section
See gh-19647
This commit is contained in:
@@ -58,6 +58,22 @@ public class ModelAssertionTests {
|
||||
.alwaysExpect(status().isOk())
|
||||
.build();
|
||||
|
||||
@Test
|
||||
void name() throws Exception {
|
||||
|
||||
EntityExchangeResult<Void> result = client.get().uri("/path")
|
||||
.exchange()
|
||||
.expectBody().isEmpty();
|
||||
|
||||
MockMvcWebTestClient.resultActionsFor(result)
|
||||
.andExpect(model().attribute("integer", 3))
|
||||
.andExpect(model().attribute("string", "a string value"))
|
||||
.andExpect(model().attribute("integer", equalTo(3))) // Hamcrest...
|
||||
.andExpect(model().attribute("string", equalTo("a string value")))
|
||||
.andExpect(model().attribute("globalAttrName", equalTo("Global Attribute Value")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void attributeEqualTo() throws Exception {
|
||||
performRequest(HttpMethod.GET, "/")
|
||||
|
||||
@@ -29,9 +29,10 @@ of several mock server setup choices or a connection to a live server.
|
||||
This setup allows you to test specific controller(s) via mock request and response objects,
|
||||
without a running server.
|
||||
|
||||
For WebFlux applications, use the below which loads the
|
||||
<<web-reactive.adoc#webflux-config, WebFlux Java configuration>> and registers the given
|
||||
controller(s) to handle requests with:
|
||||
For WebFlux applications, use the below 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 with:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -45,9 +46,11 @@ controller(s) to handle requests with:
|
||||
val client = WebTestClient.bindToController(TestController()).build()
|
||||
----
|
||||
|
||||
For Spring MVC, use the below which loads infrastructure equivalent to the
|
||||
<<web.adoc#mvc-config, WebMvc Java config>> and registers the given controller(s) to
|
||||
handle requests with via <<testing.adoc#spring-mvc-test-framework, MockMvc>>:
|
||||
For Spring MVC, use the below which delegates to the
|
||||
{api-spring-framework}/https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/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 with:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -66,13 +69,14 @@ handle requests with via <<testing.adoc#spring-mvc-test-framework, MockMvc>>:
|
||||
[[webtestclient-context-config]]
|
||||
=== Bind to `ApplicationContext`
|
||||
|
||||
This setup allows you to point to Spring configuration with Spring MVC or Spring WebFlux
|
||||
infrastructure and controller declarations which is then exercised via mock request and
|
||||
response objects, without a running server.
|
||||
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 below in which the given Spring `ApplicationContext` is passed to
|
||||
`WebHttpHandlerBuilder` to create the
|
||||
<<web-reactive.adoc#webflux-web-handler-api, WebHandler chain>> to handle requests with:
|
||||
For WebFlux, use the below 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 with:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -110,9 +114,10 @@ For WebFlux, use the below in which the given Spring `ApplicationContext` is pas
|
||||
<2> Inject the configuration
|
||||
<3> Create the `WebTestClient`
|
||||
|
||||
For Spring MVC, use the below in which the given Spring `ApplicationContext` is passed
|
||||
to `MockMvcBuilders#webAppContextSetup` to create a
|
||||
<<testing.adoc#spring-mvc-test-framework, MockMvc>> to handle requests with:
|
||||
For Spring MVC, use the below where the Spring `ApplicationContext` is passed to
|
||||
{api-spring-framework}/test/web/servlet/setup/MockMvcBuilders.html#webAppContextSetup-org.springframework.web.context.WebApplicationContext-[MockMvcBuilders.html#webAppContextSetup]
|
||||
to create a <<testing.adoc#spring-mvc-test-framework, MockMvc>> instance to handle
|
||||
requests with:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -198,7 +203,7 @@ For Spring MVC there is currently no options to test
|
||||
[[webtestclient-server-config]]
|
||||
=== Bind to Server
|
||||
|
||||
The following server setup option lets you connect to a running 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
|
||||
@@ -245,10 +250,14 @@ are readily available following `bindToServer`. For all others, you need to use
|
||||
== 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()`. What follows after
|
||||
`exchange()` is a chained API workflow to verify responses.
|
||||
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.
|
||||
|
||||
Typically, you start by asserting the response status and headers, as follows:
|
||||
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 below:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -269,13 +278,13 @@ Typically, you start by asserting the response status and headers, as follows:
|
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON)
|
||||
----
|
||||
|
||||
Then you specify how to decode and consume the response body:
|
||||
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.
|
||||
|
||||
Then you can use built-in assertions for the body. The following example shows one way to do so:
|
||||
And perform assertions on the resulting higher level Object(s):
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -296,7 +305,8 @@ Then you can use built-in assertions for the body. The following example shows o
|
||||
.expectBodyList<Person>().hasSize(3).contains(person)
|
||||
----
|
||||
|
||||
You can also go beyond the built-in assertions and create your own, as the following example shows:
|
||||
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
|
||||
@@ -323,7 +333,7 @@ You can also go beyond the built-in assertions and create your own, as the follo
|
||||
}
|
||||
----
|
||||
|
||||
You can also exit the workflow and get a result, as follows:
|
||||
Or you can exit the workflow and obtain an `EntityExchangeResult`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -356,27 +366,7 @@ instead of `Class<T>`.
|
||||
[[webtestclient-no-content]]
|
||||
=== No Content
|
||||
|
||||
If the response has no content (or you do not care if it does) use `Void.class`, which ensures
|
||||
that resources are released. The following example shows how to do so:
|
||||
|
||||
[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>()
|
||||
----
|
||||
|
||||
Alternatively, if you want to assert there is no response content, you can use code similar to the following:
|
||||
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
|
||||
@@ -397,13 +387,35 @@ Alternatively, if you want to assert there is no response content, you can use c
|
||||
.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
|
||||
|
||||
When you use `expectBody()`, the response is consumed as a `byte[]`. This is useful for
|
||||
raw content assertions. For example, you can use
|
||||
https://jsonassert.skyscreamer.org[JSONAssert] to verify JSON content, as follows:
|
||||
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
|
||||
@@ -424,7 +436,7 @@ https://jsonassert.skyscreamer.org[JSONAssert] to verify JSON content, as follow
|
||||
.json("{\"name\":\"Jane\"}")
|
||||
----
|
||||
|
||||
You can also use https://github.com/jayway/JsonPath[JSONPath] expressions, as follows:
|
||||
To verify JSON content with https://github.com/jayway/JsonPath[JSONPath]:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -452,9 +464,9 @@ You can also use https://github.com/jayway/JsonPath[JSONPath] expressions, as fo
|
||||
[[webtestclient-stream]]
|
||||
=== Streaming Responses
|
||||
|
||||
To test infinite streams (for example, `"text/event-stream"` or `"application/x-ndjson"`),
|
||||
you need to exit the chained API (by using `returnResult`), immediately after the response status
|
||||
and header assertions, as the following example shows:
|
||||
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
|
||||
@@ -478,9 +490,7 @@ and header assertions, as the following example shows:
|
||||
.returnResult<MyEvent>()
|
||||
----
|
||||
|
||||
Now you can consume the `Flux<T>`, assert decoded objects as they come, and then
|
||||
cancel at some point when test objectives are met. We recommend using the `StepVerifier`
|
||||
from the `reactor-test` module to do that, as the following example shows:
|
||||
Now you're ready to consume the response stream with `StepVerifier` from `reactor-test`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -508,12 +518,61 @@ from the `reactor-test` module to do that, as the following example shows:
|
||||
----
|
||||
|
||||
|
||||
[[webtestclient-mockmvc]]
|
||||
=== MockMvc Assertions
|
||||
|
||||
[[webtestclient-request-body]]
|
||||
=== Request Body
|
||||
`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"));
|
||||
----
|
||||
|
||||
When it comes to building requests, the `WebTestClient` offers an API identical to
|
||||
the `WebClient`, and the implementation is mostly a simple pass-through. See the
|
||||
<<web-reactive.adoc#webflux-client-body, WebClient documentation>> for examples on
|
||||
how to prepare a request with a body, including submitting form data, multipart requests,
|
||||
and more.
|
||||
|
||||
Reference in New Issue
Block a user