@ -29,7 +29,6 @@ import org.reactivestreams.Publisher;
import org.springframework.context.ApplicationContext ;
import org.springframework.context.ApplicationContext ;
import org.springframework.core.ParameterizedTypeReference ;
import org.springframework.core.ParameterizedTypeReference ;
import org.springframework.core.io.buffer.DataBuffer ;
import org.springframework.format.FormatterRegistry ;
import org.springframework.format.FormatterRegistry ;
import org.springframework.http.HttpHeaders ;
import org.springframework.http.HttpHeaders ;
import org.springframework.http.HttpMethod ;
import org.springframework.http.HttpMethod ;
@ -57,20 +56,15 @@ import org.springframework.web.util.UriBuilder;
import org.springframework.web.util.UriBuilderFactory ;
import org.springframework.web.util.UriBuilderFactory ;
/ * *
/ * *
* Main entry point for testing WebFlux server endpoints with an API similar to
* Non - blocking , reactive client for testing web servers . It uses the reactive
* that of { @link WebClient } , and actually delegating to a { @code WebClient }
* { @link WebClient } internally to perform requests and provides a fluent API
* instance , but with a focus on testing .
* to verify responses .
*
*
* < p > The { @code WebTestClient } has 3 setup options without a running server :
* < p > { @code WebTestClient } can connect to any server over an HTTP connection .
* < ul >
* It can also bind directly to WebFlux applications using mock request and
* < li > { @link # bindToController }
* response objects , without the need for an HTTP server .
* < li > { @link # bindToApplicationContext }
*
* < li > { @link # bindToRouterFunction }
* < p > See the static { @code bindToXxx } entry points for creating an instance .
* < / ul >
* < p > and 1 option for actual requests on a socket :
* < ul >
* < li > { @link # bindToServer ( ) }
* < / ul >
*
*
* @author Rossen Stoyanchev
* @author Rossen Stoyanchev
* @since 5 . 0
* @since 5 . 0
@ -156,50 +150,69 @@ public interface WebTestClient {
// Static, factory methods
// Static, factory methods
/ * *
/ * *
* Integration testing with a "mock" server targeting specific annotated ,
* Use this server setup to test one ` @Controller ` at a time .
* WebFlux controllers . The default configuration is the same as for
* This option loads the default configuration of
* { @link org . springframework . web . reactive . config . EnableWebFlux @EnableWebFlux }
* { @link org . springframework . web . reactive . config . EnableWebFlux @EnableWebFlux } .
* but can also be further customized through the returned spec .
* There are builder methods to customize the Java config . The resulting
* @param controllers the controllers to test
* WebFlux application will be tested without an HTTP server using a mock
* @return spec for setting up controller configuration
* request and response .
* @param controllers one or more controller instances to tests
* @return chained API to customize server and client config ; use
* { @link MockServerSpec # configureClient ( ) } to transition to client config
* /
* /
static ControllerSpec bindToController ( Object . . . controllers ) {
static ControllerSpec bindToController ( Object . . . controllers ) {
return new DefaultControllerSpec ( controllers ) ;
return new DefaultControllerSpec ( controllers ) ;
}
}
/ * *
/ * *
* Integration testing with a "mock" server with WebFlux infrastructure
* Use this option to set up a server from a { @link RouterFunction } .
* detected from an { @link ApplicationContext } such as
* Internally the provided configuration is passed to
* { @code @EnableWebFlux } Java config and annotated controller Spring beans .
* { @code RouterFunctions # toWebHandler } . The resulting WebFlux application
* @param applicationContext the context
* will be tested without an HTTP server using a mock request and response .
* @return the { @code WebTestClient } builder
* @param routerFunction the RouterFunction to test
* @see org . springframework . web . reactive . config . EnableWebFlux
* @return chained API to customize server and client config ; use
* { @link MockServerSpec # configureClient ( ) } to transition to client config
* /
* /
static MockServerSpec < ? > bindToApplicationContext ( ApplicationContext applicationContext ) {
static RouterFunctionSpec bindToRouterFunction ( RouterFunction < ? > routerFunction ) {
return new ApplicationContextSpec ( applicationContext ) ;
return new DefaultRouterFunctionSpec ( routerFunction ) ;
}
}
/ * *
/ * *
* Integration testing without a server targeting WebFlux functional endpoints .
* Use this option to setup a server from the Spring configuration of your
* @param routerFunction the RouterFunction to test
* application , or some subset of it . Internally the provided configuration
* @return the { @code WebTestClient } builder
* is passed to { @code WebHttpHandlerBuilder } to set up the request
* processing chain . The resulting WebFlux application will be tested
* without an HTTP server using a mock request and response .
* < p > Consider using the TestContext framework and
* { @link org . springframework . test . context . ContextConfiguration @ContextConfiguration }
* in order to efficently load and inject the Spring configuration into the
* test class .
* @param applicationContext the Spring context
* @return chained API to customize server and client config ; use
* { @link MockServerSpec # configureClient ( ) } to transition to client config
* /
* /
static RouterFunctionSpec bindToRouterFunction ( RouterFunction < ? > routerFunction ) {
static MockServerSpec < ? > bindToApplicationContext ( ApplicationContext applicationContext ) {
return new DefaultRouterFunctionSpec ( routerFunction ) ;
return new ApplicationContextSpec ( applicationContext ) ;
}
}
/ * *
/ * *
* Integration testing with a "mock" server targeting the given WebHandler .
* Integration testing with a "mock" server targeting the given WebHandler .
* @param webHandler the handler to test
* @param webHandler the handler to test
* @return the { @code WebTestClient } builder
* @return chained API to customize server and client config ; use
* { @link MockServerSpec # configureClient ( ) } to transition to client config
* /
* /
static MockServerSpec < ? > bindToWebHandler ( WebHandler webHandler ) {
static MockServerSpec < ? > bindToWebHandler ( WebHandler webHandler ) {
return new DefaultMockServerSpec ( webHandler ) ;
return new DefaultMockServerSpec ( webHandler ) ;
}
}
/ * *
/ * *
* Complete end - to - end integration tests with actual requests to a running server .
* This server setup option allows you to connect to a running server .
* @return the { @code WebTestClient } builder
* < p > < pre class = "code" >
* WebTestClient client = WebTestClient . bindToServer ( )
* . baseUrl ( "http://localhost:8080" )
* . build ( ) ;
* < / pre >
* @return chained API to customize client config
* /
* /
static Builder bindToServer ( ) {
static Builder bindToServer ( ) {
return new DefaultWebTestClientBuilder ( ) ;
return new DefaultWebTestClientBuilder ( ) ;
@ -609,70 +622,73 @@ public interface WebTestClient {
/ * *
/ * *
* Spec for declaring expectations on the response .
* Chained API for applying assertions to a response .
* /
* /
interface ResponseSpec {
interface ResponseSpec {
/ * *
/ * *
* Declare expecta tions on the response status .
* Asser tions on the response status .
* /
* /
StatusAssertions expectStatus ( ) ;
StatusAssertions expectStatus ( ) ;
/ * *
/ * *
* Declared expecta tions on the headers of the response .
* Asser tions on the headers of the response .
* /
* /
HeaderAssertions expectHeader ( ) ;
HeaderAssertions expectHeader ( ) ;
/ * *
/ * *
* Declare expectations on the response body decoded to { @code < B > } .
* Consume and decode the response body to a single object of type
* { @code < B > } and then apply assertions .
* @param bodyType the expected body type
* @param bodyType the expected body type
* /
* /
< B > BodySpec < B , ? > expectBody ( Class < B > bodyType ) ;
< B > BodySpec < B , ? > expectBody ( Class < B > bodyType ) ;
/ * *
/ * *
* Variant of { @link # expectBody ( Class ) } for a body type with generics .
* Alternative to { @link # expectBody ( Class ) } that accepts information
* about a target type with generics .
* /
* /
< B > BodySpec < B , ? > expectBody ( ParameterizedTypeReference < B > bodyType ) ;
< B > BodySpec < B , ? > expectBody ( ParameterizedTypeReference < B > bodyType ) ;
/ * *
/ * *
* Declare expectations on the response body decoded to { @code List < E > } .
* Consume and decode the response body to { @code List < E > } and then apply
* List - specific assertions .
* @param elementType the expected List element type
* @param elementType the expected List element type
* /
* /
< E > ListBodySpec < E > expectBodyList ( Class < E > elementType ) ;
< E > ListBodySpec < E > expectBodyList ( Class < E > elementType ) ;
/ * *
/ * *
* Variant of { @link # expectBodyList ( Class ) } for element types with generics .
* Alternative to { @link # expectBodyList ( Class ) } that accepts information
* about a target type with generics .
* /
* /
< E > ListBodySpec < E > expectBodyList ( ParameterizedTypeReference < E > elementType ) ;
< E > ListBodySpec < E > expectBodyList ( ParameterizedTypeReference < E > elementType ) ;
/ * *
/ * *
* Declare expectations on the response body content .
* Consume and decode the response body to { @code byte [ ] } and then apply
* assertions on the raw content ( e . g . isEmpty , JSONPath , etc . )
* /
* /
BodyContentSpec expectBody ( ) ;
BodyContentSpec expectBody ( ) ;
/ * *
/ * *
* Return the exchange result with the body decoded to { @code Flux < T > } .
* Exit the chained API and consume the response body externally . This
* Use this option for infinite streams and consume the stream with
* is useful for testing infinite streams ( e . g . SSE ) where you need to
* the { @code StepVerifier } from the Reactor Add - Ons .
* to assert decoded objects as they come and then cancel at some point
* when test objectives are met . Consider using { @code StepVerifier }
* from { @literal "reactor-test" } to assert the { @code Flux < T > } stream
* of decoded objects .
*
*
* @see < a href = "https://github.com/reactor/reactor-addons" >
* < p > < strong > Note : < / strong > Do not use this option for cases where there
* https : //github.com/reactor/reactor-addons</a>
* is no content ( e . g . 204 , 4xx ) or you ' re not interested in the content .
* For such cases you can use { @code expectBody ( ) . isEmpty ( ) } or
* { @code expectBody ( Void . class ) } which ensures that resources are
* released regardless of whether the response has content or not .
* /
* /
< T > FluxExchangeResult < T > returnResult ( Class < T > elementType ) ;
< T > FluxExchangeResult < T > returnResult ( Class < T > elementType ) ;
/ * *
/ * *
* Variant of { @link # returnResult ( Class ) } for element types with generics .
* Alternative to { @link # returnResult ( Class ) } that accepts information
* about a target type with generics .
* /
* /
< T > FluxExchangeResult < T > returnResult ( ParameterizedTypeReference < T > elementType ) ;
< T > FluxExchangeResult < T > returnResult ( ParameterizedTypeReference < T > elementType ) ;
/ * *
* Return the exchange result with the body decoded to
* { @code Flux < DataBuffer > } . Use this option for infinite streams and
* consume the stream with the { @code StepVerifier } from the Reactor Add - Ons .
*
* @return
* /
FluxExchangeResult < DataBuffer > returnResult ( ) ;
}
}
/ * *
/ * *
@ -691,7 +707,8 @@ public interface WebTestClient {
< T extends S > T consumeWith ( Consumer < EntityExchangeResult < B > > consumer ) ;
< T extends S > T consumeWith ( Consumer < EntityExchangeResult < B > > consumer ) ;
/ * *
/ * *
* Return the exchange result with the decoded body .
* Exit the chained API and return an { @code ExchangeResult } with the
* decoded response content .
* /
* /
EntityExchangeResult < B > returnResult ( ) ;
EntityExchangeResult < B > returnResult ( ) ;
@ -763,7 +780,8 @@ public interface WebTestClient {
BodyContentSpec consumeWith ( Consumer < EntityExchangeResult < byte [ ] > > consumer ) ;
BodyContentSpec consumeWith ( Consumer < EntityExchangeResult < byte [ ] > > consumer ) ;
/ * *
/ * *
* Return the exchange result with body content as { @code byte [ ] } .
* Exit the chained API and return an { @code ExchangeResult } with the
* raw response content .
* /
* /
EntityExchangeResult < byte [ ] > returnResult ( ) ;
EntityExchangeResult < byte [ ] > returnResult ( ) ;