Add Kotlin code snippets to WebTestClient testing refdoc
Closes gh-21778
This commit is contained in:
parent
b20a8c9d98
commit
3dfd0a24dc
|
@ -27,11 +27,16 @@ a URL to connect to a running server.
|
|||
|
||||
The following example shows how to create a server setup to test one `@Controller` at a time:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
client = WebTestClient.bindToController(new TestController()).build();
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
client = WebTestClient.bindToController(TestController()).build()
|
||||
----
|
||||
|
||||
The preceding example loads the <<web-reactive.adoc#webflux-config, WebFlux Java configuration>>
|
||||
and registers the given controller. The resulting WebFlux application is tested
|
||||
|
@ -46,12 +51,18 @@ on the builder to customize the default WebFlux Java configuration.
|
|||
The following example shows how to set up a server from a
|
||||
<<web-reactive.adoc#webflux-fn, RouterFunction>>:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[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()
|
||||
----
|
||||
|
||||
Internally, the configuration is passed to `RouterFunctions.toWebHandler`.
|
||||
The resulting WebFlux application is tested without an HTTP server by using mock
|
||||
|
@ -65,11 +76,10 @@ request and response objects.
|
|||
The following example shows how to setup a server from the Spring configuration of your application or
|
||||
some subset of it:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = WebConfig.class) // <1>
|
||||
@SpringJUnitConfig(WebConfig.class) // <1>
|
||||
public class MyTests {
|
||||
|
||||
@Autowired
|
||||
|
@ -77,13 +87,33 @@ some subset of it:
|
|||
|
||||
private WebTestClient client;
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
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 {
|
||||
|
||||
@Autowired
|
||||
lateinit var context: ApplicationContext // <2>
|
||||
|
||||
lateinit var client: WebTestClient
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
client = WebTestClient.bindToApplicationContext(context).build() // <3>
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Specify the configuration to load
|
||||
<2> Inject the configuration
|
||||
<3> Create the `WebTestClient`
|
||||
|
@ -100,11 +130,16 @@ using mock request and response objects.
|
|||
|
||||
The following server setup option lets you connect to a running server:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[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()
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -116,14 +151,22 @@ options, including base URL, default headers, client filters, and others. These
|
|||
are readily available following `bindToServer`. For all others, you need to use
|
||||
`configureClient()` to transition from server to client configuration, as follows:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[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()
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -137,15 +180,23 @@ up to the point of performing a request by using `exchange()`. What follows afte
|
|||
|
||||
Typically, you start by asserting the response status and headers, as follows:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[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)
|
||||
// ...
|
||||
----
|
||||
|
||||
Then you specify how to decode and consume the response body:
|
||||
|
@ -156,18 +207,32 @@ Then you specify how to decode and consume the response body:
|
|||
|
||||
Then you can use built-in assertions for the body. The following example shows one way to do so:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[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)
|
||||
----
|
||||
|
||||
You can also go beyond the built-in assertions and create your own, as the following example shows:
|
||||
|
||||
[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()
|
||||
|
@ -176,9 +241,22 @@ You can also go beyond the built-in assertions and create your own, as the follo
|
|||
// 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)...
|
||||
}
|
||||
----
|
||||
|
||||
You can also exit the workflow and get a result, as follows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
EntityExchangeResult<Person> result = client.get().uri("/persons/1")
|
||||
.exchange()
|
||||
|
@ -186,6 +264,17 @@ You can also exit the workflow and get a result, as follows:
|
|||
.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
|
||||
|
@ -200,19 +289,27 @@ instead of `Class<T>`.
|
|||
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,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[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:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
client.post().uri("/persons")
|
||||
.body(personMono, Person.class)
|
||||
|
@ -220,7 +317,15 @@ Alternatively, if you want to assert there is no response content, you can use c
|
|||
.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()
|
||||
----
|
||||
|
||||
|
||||
[[webtestclient-json]]
|
||||
|
@ -230,8 +335,17 @@ When you use `expectBody()`, the response is consumed as a `byte[]`. This is use
|
|||
raw content assertions. For example, you can use
|
||||
https://jsonassert.skyscreamer.org[JSONAssert] to verify JSON content, as follows:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[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()
|
||||
|
@ -242,8 +356,8 @@ https://jsonassert.skyscreamer.org[JSONAssert] to verify JSON content, as follow
|
|||
|
||||
You can also use https://github.com/jayway/JsonPath[JSONPath] expressions, as follows:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
client.get().uri("/persons")
|
||||
.exchange()
|
||||
|
@ -252,6 +366,16 @@ You can also use https://github.com/jayway/JsonPath[JSONPath] expressions, as fo
|
|||
.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")
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -262,8 +386,8 @@ To test infinite streams (for example, `"text/event-stream"` or `"application/st
|
|||
you need to exit the chained API (by using `returnResult`), immediately after the response status
|
||||
and header assertions, as the following example shows:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
FluxExchangeResult<MyEvent> result = client.get().uri("/events")
|
||||
.accept(TEXT_EVENT_STREAM)
|
||||
|
@ -272,15 +396,26 @@ and header assertions, as the following example shows:
|
|||
.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 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:
|
||||
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
Flux<Event> eventFux = result.getResponseBody();
|
||||
Flux<Event> eventFlux = result.getResponseBody();
|
||||
|
||||
StepVerifier.create(eventFlux)
|
||||
.expectNext(person)
|
||||
|
@ -289,6 +424,18 @@ from the `reactor-test` module to do that, as the following example shows:
|
|||
.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()
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue