More updates to Web testing section

See gh-19647
This commit is contained in:
Rossen Stoyanchev
2020-09-01 08:56:57 +01:00
parent c6b87b3ef4
commit e34c800467
2 changed files with 136 additions and 61 deletions

View File

@@ -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, "/")

View File

@@ -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.