|
|
|
|
@@ -29,7 +29,6 @@ import org.reactivestreams.Publisher;
|
|
|
|
|
|
|
|
|
|
import org.springframework.context.ApplicationContext;
|
|
|
|
|
import org.springframework.core.ParameterizedTypeReference;
|
|
|
|
|
import org.springframework.core.io.buffer.DataBuffer;
|
|
|
|
|
import org.springframework.format.FormatterRegistry;
|
|
|
|
|
import org.springframework.http.HttpHeaders;
|
|
|
|
|
import org.springframework.http.HttpMethod;
|
|
|
|
|
@@ -57,20 +56,15 @@ import org.springframework.web.util.UriBuilder;
|
|
|
|
|
import org.springframework.web.util.UriBuilderFactory;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Main entry point for testing WebFlux server endpoints with an API similar to
|
|
|
|
|
* that of {@link WebClient}, and actually delegating to a {@code WebClient}
|
|
|
|
|
* instance, but with a focus on testing.
|
|
|
|
|
* Non-blocking, reactive client for testing web servers. It uses the reactive
|
|
|
|
|
* {@link WebClient} internally to perform requests and provides a fluent API
|
|
|
|
|
* to verify responses.
|
|
|
|
|
*
|
|
|
|
|
* <p>The {@code WebTestClient} has 3 setup options without a running server:
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #bindToController}
|
|
|
|
|
* <li>{@link #bindToApplicationContext}
|
|
|
|
|
* <li>{@link #bindToRouterFunction}
|
|
|
|
|
* </ul>
|
|
|
|
|
* <p>and 1 option for actual requests on a socket:
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #bindToServer()}
|
|
|
|
|
* </ul>
|
|
|
|
|
* <p>{@code WebTestClient} can connect to any server over an HTTP connection.
|
|
|
|
|
* It can also bind directly to WebFlux applications using mock request and
|
|
|
|
|
* response objects, without the need for an HTTP server.
|
|
|
|
|
*
|
|
|
|
|
* <p>See the static {@code bindToXxx} entry points for creating an instance.
|
|
|
|
|
*
|
|
|
|
|
* @author Rossen Stoyanchev
|
|
|
|
|
* @since 5.0
|
|
|
|
|
@@ -156,50 +150,69 @@ public interface WebTestClient {
|
|
|
|
|
// Static, factory methods
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Integration testing with a "mock" server targeting specific annotated,
|
|
|
|
|
* WebFlux controllers. The default configuration is the same as for
|
|
|
|
|
* {@link org.springframework.web.reactive.config.EnableWebFlux @EnableWebFlux}
|
|
|
|
|
* but can also be further customized through the returned spec.
|
|
|
|
|
* @param controllers the controllers to test
|
|
|
|
|
* @return spec for setting up controller configuration
|
|
|
|
|
* Use this server setup to test one `@Controller` at a time.
|
|
|
|
|
* This option loads the default configuration of
|
|
|
|
|
* {@link org.springframework.web.reactive.config.EnableWebFlux @EnableWebFlux}.
|
|
|
|
|
* There are builder methods to customize the Java config. The resulting
|
|
|
|
|
* WebFlux application will be tested without an HTTP server using a mock
|
|
|
|
|
* 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) {
|
|
|
|
|
return new DefaultControllerSpec(controllers);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Integration testing with a "mock" server with WebFlux infrastructure
|
|
|
|
|
* detected from an {@link ApplicationContext} such as
|
|
|
|
|
* {@code @EnableWebFlux} Java config and annotated controller Spring beans.
|
|
|
|
|
* @param applicationContext the context
|
|
|
|
|
* @return the {@code WebTestClient} builder
|
|
|
|
|
* @see org.springframework.web.reactive.config.EnableWebFlux
|
|
|
|
|
*/
|
|
|
|
|
static MockServerSpec<?> bindToApplicationContext(ApplicationContext applicationContext) {
|
|
|
|
|
return new ApplicationContextSpec(applicationContext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Integration testing without a server targeting WebFlux functional endpoints.
|
|
|
|
|
* Use this option to set up a server from a {@link RouterFunction}.
|
|
|
|
|
* Internally the provided configuration is passed to
|
|
|
|
|
* {@code RouterFunctions#toWebHandler}. The resulting WebFlux application
|
|
|
|
|
* will be tested without an HTTP server using a mock request and response.
|
|
|
|
|
* @param routerFunction the RouterFunction 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 RouterFunctionSpec bindToRouterFunction(RouterFunction<?> routerFunction) {
|
|
|
|
|
return new DefaultRouterFunctionSpec(routerFunction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Use this option to setup a server from the Spring configuration of your
|
|
|
|
|
* application, or some subset of it. Internally the provided configuration
|
|
|
|
|
* 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 MockServerSpec<?> bindToApplicationContext(ApplicationContext applicationContext) {
|
|
|
|
|
return new ApplicationContextSpec(applicationContext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Integration testing with a "mock" server targeting the given WebHandler.
|
|
|
|
|
* @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) {
|
|
|
|
|
return new DefaultMockServerSpec(webHandler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Complete end-to-end integration tests with actual requests to a running server.
|
|
|
|
|
* @return the {@code WebTestClient} builder
|
|
|
|
|
* This server setup option allows you to connect to a running server.
|
|
|
|
|
* <p><pre class="code">
|
|
|
|
|
* WebTestClient client = WebTestClient.bindToServer()
|
|
|
|
|
* .baseUrl("http://localhost:8080")
|
|
|
|
|
* .build();
|
|
|
|
|
* </pre>
|
|
|
|
|
* @return chained API to customize client config
|
|
|
|
|
*/
|
|
|
|
|
static Builder bindToServer() {
|
|
|
|
|
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 {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Declare expectations on the response status.
|
|
|
|
|
* Assertions on the response status.
|
|
|
|
|
*/
|
|
|
|
|
StatusAssertions expectStatus();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Declared expectations on the headers of the response.
|
|
|
|
|
* Assertions on the headers of the response.
|
|
|
|
|
*/
|
|
|
|
|
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
|
|
|
|
|
*/
|
|
|
|
|
<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);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
<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);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the exchange result with the body decoded to {@code Flux<T>}.
|
|
|
|
|
* Use this option for infinite streams and consume the stream with
|
|
|
|
|
* the {@code StepVerifier} from the Reactor Add-Ons.
|
|
|
|
|
* Exit the chained API and consume the response body externally. This
|
|
|
|
|
* is useful for testing infinite streams (e.g. SSE) where you need to
|
|
|
|
|
* 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">
|
|
|
|
|
* https://github.com/reactor/reactor-addons</a>
|
|
|
|
|
* <p><strong>Note:</strong> Do not use this option for cases where there
|
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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();
|
|
|
|
|
|
|
|
|
|
@@ -763,7 +780,8 @@ public interface WebTestClient {
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
|