001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.test.web.reactive.server;
018
019import java.net.URI;
020import java.nio.charset.Charset;
021import java.time.Duration;
022import java.time.ZonedDateTime;
023import java.util.List;
024import java.util.Map;
025import java.util.function.Consumer;
026import java.util.function.Function;
027
028import org.hamcrest.Matcher;
029import org.reactivestreams.Publisher;
030
031import org.springframework.context.ApplicationContext;
032import org.springframework.core.ParameterizedTypeReference;
033import org.springframework.core.ReactiveAdapterRegistry;
034import org.springframework.format.FormatterRegistry;
035import org.springframework.http.HttpHeaders;
036import org.springframework.http.HttpMethod;
037import org.springframework.http.MediaType;
038import org.springframework.http.client.reactive.ClientHttpConnector;
039import org.springframework.http.client.reactive.ClientHttpRequest;
040import org.springframework.http.codec.ClientCodecConfigurer;
041import org.springframework.http.codec.ServerCodecConfigurer;
042import org.springframework.lang.Nullable;
043import org.springframework.util.MultiValueMap;
044import org.springframework.validation.Validator;
045import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
046import org.springframework.web.reactive.config.CorsRegistry;
047import org.springframework.web.reactive.config.PathMatchConfigurer;
048import org.springframework.web.reactive.config.ViewResolverRegistry;
049import org.springframework.web.reactive.config.WebFluxConfigurer;
050import org.springframework.web.reactive.function.BodyInserter;
051import org.springframework.web.reactive.function.BodyInserters;
052import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
053import org.springframework.web.reactive.function.client.ExchangeStrategies;
054import org.springframework.web.reactive.function.client.WebClient;
055import org.springframework.web.reactive.function.server.HandlerStrategies;
056import org.springframework.web.reactive.function.server.RouterFunction;
057import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
058import org.springframework.web.server.WebFilter;
059import org.springframework.web.server.WebHandler;
060import org.springframework.web.server.session.WebSessionManager;
061import org.springframework.web.util.UriBuilder;
062import org.springframework.web.util.UriBuilderFactory;
063
064/**
065 * Client for testing web servers that uses {@link WebClient} internally to
066 * perform requests while also providing a fluent API to verify responses.
067 * This client can connect to any server over HTTP, or to a WebFlux application
068 * via mock request and response objects.
069 *
070 * <p>Use one of the bindToXxx methods to create an instance. For example:
071 * <ul>
072 * <li>{@link #bindToController(Object...)}
073 * <li>{@link #bindToRouterFunction(RouterFunction)}
074 * <li>{@link #bindToApplicationContext(ApplicationContext)}
075 * <li>{@link #bindToServer()}
076 * <li>...
077 * </ul>
078 *
079 * <p><strong>Warning</strong>: {@code WebTestClient} is not usable yet in
080 * Kotlin due to a <a href="https://youtrack.jetbrains.com/issue/KT-5464">type inference issue</a>
081 * which is expected to be fixed as of Kotlin 1.3. You can watch
082 * <a href="https://github.com/spring-projects/spring-framework/issues/20606">gh-20606</a>
083 * for up-to-date information. Meanwhile, the proposed alternative is to use
084 * directly {@link WebClient} with its Reactor and Spring Kotlin extensions to
085 * perform integration tests on an embedded WebFlux server.
086 *
087 * @author Rossen Stoyanchev
088 * @author Brian Clozel
089 * @since 5.0
090 * @see StatusAssertions
091 * @see HeaderAssertions
092 * @see JsonPathAssertions
093 */
094public interface WebTestClient {
095
096        /**
097         * The name of a request header used to assign a unique id to every request
098         * performed through the {@code WebTestClient}. This can be useful for
099         * storing contextual information at all phases of request processing (e.g.
100         * from a server-side component) under that id and later to look up
101         * that information once an {@link ExchangeResult} is available.
102         */
103        String WEBTESTCLIENT_REQUEST_ID = "WebTestClient-Request-Id";
104
105
106        /**
107         * Prepare an HTTP GET request.
108         * @return a spec for specifying the target URL
109         */
110        RequestHeadersUriSpec<?> get();
111
112        /**
113         * Prepare an HTTP HEAD request.
114         * @return a spec for specifying the target URL
115         */
116        RequestHeadersUriSpec<?> head();
117
118        /**
119         * Prepare an HTTP POST request.
120         * @return a spec for specifying the target URL
121         */
122        RequestBodyUriSpec post();
123
124        /**
125         * Prepare an HTTP PUT request.
126         * @return a spec for specifying the target URL
127         */
128        RequestBodyUriSpec put();
129
130        /**
131         * Prepare an HTTP PATCH request.
132         * @return a spec for specifying the target URL
133         */
134        RequestBodyUriSpec patch();
135
136        /**
137         * Prepare an HTTP DELETE request.
138         * @return a spec for specifying the target URL
139         */
140        RequestHeadersUriSpec<?> delete();
141
142        /**
143         * Prepare an HTTP OPTIONS request.
144         * @return a spec for specifying the target URL
145         */
146        RequestHeadersUriSpec<?> options();
147
148        /**
149         * Prepare a request for the specified {@code HttpMethod}.
150         * @return a spec for specifying the target URL
151         */
152        RequestBodyUriSpec method(HttpMethod method);
153
154
155        /**
156         * Return a builder to mutate properties of this web test client.
157         */
158        Builder mutate();
159
160        /**
161         * Mutate the {@link WebTestClient}, apply the given configurer, and build
162         * a new instance. Essentially a shortcut for:
163         * <pre>
164         * mutate().apply(configurer).build();
165         * </pre>
166         * @param configurer the configurer to apply
167         * @return the mutated test client
168         */
169        WebTestClient mutateWith(WebTestClientConfigurer configurer);
170
171
172        // Static factory methods
173
174        /**
175         * Use this server setup to test one `@Controller` at a time.
176         * This option loads the default configuration of
177         * {@link org.springframework.web.reactive.config.EnableWebFlux @EnableWebFlux}.
178         * There are builder methods to customize the Java config. The resulting
179         * WebFlux application will be tested without an HTTP server using a mock
180         * request and response.
181         * @param controllers one or more controller instances to tests
182         * (specified {@code Class} will be turned into instance)
183         * @return chained API to customize server and client config; use
184         * {@link MockServerSpec#configureClient()} to transition to client config
185         */
186        static ControllerSpec bindToController(Object... controllers) {
187                return new DefaultControllerSpec(controllers);
188        }
189
190        /**
191         * Use this option to set up a server from a {@link RouterFunction}.
192         * Internally the provided configuration is passed to
193         * {@code RouterFunctions#toWebHandler}. The resulting WebFlux application
194         * will be tested without an HTTP server using a mock request and response.
195         * @param routerFunction the RouterFunction to test
196         * @return chained API to customize server and client config; use
197         * {@link MockServerSpec#configureClient()} to transition to client config
198         */
199        static RouterFunctionSpec bindToRouterFunction(RouterFunction<?> routerFunction) {
200                return new DefaultRouterFunctionSpec(routerFunction);
201        }
202
203        /**
204         * Use this option to setup a server from the Spring configuration of your
205         * application, or some subset of it. Internally the provided configuration
206         * is passed to {@code WebHttpHandlerBuilder} to set up the request
207         * processing chain. The resulting WebFlux application will be tested
208         * without an HTTP server using a mock request and response.
209         * <p>Consider using the TestContext framework and
210         * {@link org.springframework.test.context.ContextConfiguration @ContextConfiguration}
211         * in order to efficiently load and inject the Spring configuration into the
212         * test class.
213         * @param applicationContext the Spring context
214         * @return chained API to customize server and client config; use
215         * {@link MockServerSpec#configureClient()} to transition to client config
216         */
217        static MockServerSpec<?> bindToApplicationContext(ApplicationContext applicationContext) {
218                return new ApplicationContextSpec(applicationContext);
219        }
220
221        /**
222         * Integration testing with a "mock" server targeting the given WebHandler.
223         * @param webHandler the handler to test
224         * @return chained API to customize server and client config; use
225         * {@link MockServerSpec#configureClient()} to transition to client config
226         */
227        static MockServerSpec<?> bindToWebHandler(WebHandler webHandler) {
228                return new DefaultMockServerSpec(webHandler);
229        }
230
231        /**
232         * This server setup option allows you to connect to a running server via
233         * Reactor Netty.
234         * <p><pre class="code">
235         * WebTestClient client = WebTestClient.bindToServer()
236         *         .baseUrl("http://localhost:8080")
237         *         .build();
238         * </pre>
239         * @return chained API to customize client config
240         */
241        static Builder bindToServer() {
242                return new DefaultWebTestClientBuilder();
243        }
244
245        /**
246         * A variant of {@link #bindToServer()} with a pre-configured connector.
247         * <p><pre class="code">
248         * WebTestClient client = WebTestClient.bindToServer()
249         *         .baseUrl("http://localhost:8080")
250         *         .build();
251         * </pre>
252         * @return chained API to customize client config
253         * @since 5.0.2
254         */
255        static Builder bindToServer(ClientHttpConnector connector) {
256                return new DefaultWebTestClientBuilder(connector);
257        }
258
259
260        /**
261         * Base specification for setting up tests without a server.
262         *
263         * @param <B> a self reference to the builder type
264         */
265        interface MockServerSpec<B extends MockServerSpec<B>> {
266
267                /**
268                 * Register {@link WebFilter} instances to add to the mock server.
269                 * @param filter one or more filters
270                 */
271                <T extends B> T webFilter(WebFilter... filter);
272
273                /**
274                 * Provide a session manager instance for the mock server.
275                 * <p>By default an instance of
276                 * {@link org.springframework.web.server.session.DefaultWebSessionManager
277                 * DefaultWebSessionManager} is used.
278                 * @param sessionManager the session manager to use
279                 */
280                <T extends B> T webSessionManager(WebSessionManager sessionManager);
281
282                /**
283                 * Shortcut for pre-packaged customizations to the mock server setup.
284                 * @param configurer the configurer to apply
285                 */
286                <T extends B> T apply(MockServerConfigurer configurer);
287
288                /**
289                 * Proceed to configure and build the test client.
290                 */
291                Builder configureClient();
292
293                /**
294                 * Shortcut to build the test client.
295                 */
296                WebTestClient build();
297        }
298
299
300        /**
301         * Specification for customizing controller configuration equivalent to, and
302         * internally delegating to, a {@link WebFluxConfigurer}.
303         */
304        interface ControllerSpec extends MockServerSpec<ControllerSpec> {
305
306                /**
307                 * Register one or more {@link org.springframework.web.bind.annotation.ControllerAdvice}
308                 * instances to be used in tests (specified {@code Class} will be turned into instance).
309                 */
310                ControllerSpec controllerAdvice(Object... controllerAdvice);
311
312                /**
313                 * Customize content type resolution.
314                 * @see WebFluxConfigurer#configureContentTypeResolver
315                 */
316                ControllerSpec contentTypeResolver(Consumer<RequestedContentTypeResolverBuilder> consumer);
317
318                /**
319                 * Configure CORS support.
320                 * @see WebFluxConfigurer#addCorsMappings
321                 */
322                ControllerSpec corsMappings(Consumer<CorsRegistry> consumer);
323
324                /**
325                 * Configure path matching options.
326                 * @see WebFluxConfigurer#configurePathMatching
327                 */
328                ControllerSpec pathMatching(Consumer<PathMatchConfigurer> consumer);
329
330                /**
331                 * Configure resolvers for custom controller method arguments.
332                 * @see WebFluxConfigurer#configureHttpMessageCodecs
333                 */
334                ControllerSpec argumentResolvers(Consumer<ArgumentResolverConfigurer> configurer);
335
336                /**
337                 * Configure custom HTTP message readers and writers or override built-in ones.
338                 * @see WebFluxConfigurer#configureHttpMessageCodecs
339                 */
340                ControllerSpec httpMessageCodecs(Consumer<ServerCodecConfigurer> configurer);
341
342                /**
343                 * Register formatters and converters to use for type conversion.
344                 * @see WebFluxConfigurer#addFormatters
345                 */
346                ControllerSpec formatters(Consumer<FormatterRegistry> consumer);
347
348                /**
349                 * Configure a global Validator.
350                 * @see WebFluxConfigurer#getValidator()
351                 */
352                ControllerSpec validator(Validator validator);
353
354                /**
355                 * Configure view resolution.
356                 * @see WebFluxConfigurer#configureViewResolvers
357                 */
358                ControllerSpec viewResolvers(Consumer<ViewResolverRegistry> consumer);
359        }
360
361
362        /**
363         * Specification for customizing router function configuration.
364         */
365        interface RouterFunctionSpec extends MockServerSpec<RouterFunctionSpec> {
366
367                /**
368                 * Configure handler strategies.
369                 */
370                RouterFunctionSpec handlerStrategies(HandlerStrategies handlerStrategies);
371        }
372
373
374        /**
375         * Steps for customizing the {@link WebClient} used to test with,
376         * internally delegating to a
377         * {@link org.springframework.web.reactive.function.client.WebClient.Builder
378         * WebClient.Builder}.
379         */
380        interface Builder {
381
382                /**
383                 * Configure a base URI as described in
384                 * {@link org.springframework.web.reactive.function.client.WebClient#create(String)
385                 * WebClient.create(String)}.
386                 */
387                Builder baseUrl(String baseUrl);
388
389                /**
390                 * Provide a pre-configured {@link UriBuilderFactory} instance as an
391                 * alternative to and effectively overriding {@link #baseUrl(String)}.
392                 */
393                Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory);
394
395                /**
396                 * Add the given header to all requests that haven't added it.
397                 * @param headerName the header name
398                 * @param headerValues the header values
399                 */
400                Builder defaultHeader(String headerName, String... headerValues);
401
402                /**
403                 * Manipulate the default headers with the given consumer. The
404                 * headers provided to the consumer are "live", so that the consumer can be used to
405                 * {@linkplain HttpHeaders#set(String, String) overwrite} existing header values,
406                 * {@linkplain HttpHeaders#remove(Object) remove} values, or use any of the other
407                 * {@link HttpHeaders} methods.
408                 * @param headersConsumer a function that consumes the {@code HttpHeaders}
409                 * @return this builder
410                 */
411                Builder defaultHeaders(Consumer<HttpHeaders> headersConsumer);
412
413                /**
414                 * Add the given header to all requests that haven't added it.
415                 * @param cookieName the cookie name
416                 * @param cookieValues the cookie values
417                 */
418                Builder defaultCookie(String cookieName, String... cookieValues);
419
420                /**
421                 * Manipulate the default cookies with the given consumer. The
422                 * map provided to the consumer is "live", so that the consumer can be used to
423                 * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing header values,
424                 * {@linkplain MultiValueMap#remove(Object) remove} values, or use any of the other
425                 * {@link MultiValueMap} methods.
426                 * @param cookiesConsumer a function that consumes the cookies map
427                 * @return this builder
428                 */
429                Builder defaultCookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);
430
431                /**
432                 * Add the given filter to the filter chain.
433                 * @param filter the filter to be added to the chain
434                 */
435                Builder filter(ExchangeFilterFunction filter);
436
437                /**
438                 * Manipulate the filters with the given consumer. The
439                 * list provided to the consumer is "live", so that the consumer can be used to remove
440                 * filters, change ordering, etc.
441                 * @param filtersConsumer a function that consumes the filter list
442                 * @return this builder
443                 */
444                Builder filters(Consumer<List<ExchangeFilterFunction>> filtersConsumer);
445
446                /**
447                 * Configure the codecs for the {@code WebClient} in the
448                 * {@link #exchangeStrategies(ExchangeStrategies) underlying}
449                 * {@code ExchangeStrategies}.
450                 * @param configurer the configurer to apply
451                 * @since 5.1.13
452                 */
453                Builder codecs(Consumer<ClientCodecConfigurer> configurer);
454
455                /**
456                 * Configure the {@link ExchangeStrategies} to use.
457                 * <p>For most cases, prefer using {@link #codecs(Consumer)} which allows
458                 * customizing the codecs in the {@code ExchangeStrategies} rather than
459                 * replace them. That ensures multiple parties can contribute to codecs
460                 * configuration.
461                 * <p>By default this is set to {@link ExchangeStrategies#withDefaults()}.
462                 * @param strategies the strategies to use
463                 */
464                Builder exchangeStrategies(ExchangeStrategies strategies);
465
466                /**
467                 * Customize the strategies configured via
468                 * {@link #exchangeStrategies(ExchangeStrategies)}. This method is
469                 * designed for use in scenarios where multiple parties wish to update
470                 * the {@code ExchangeStrategies}.
471                 * @deprecated as of 5.1.13 in favor of {@link #codecs(Consumer)}
472                 */
473                @Deprecated
474                Builder exchangeStrategies(Consumer<ExchangeStrategies.Builder> configurer);
475
476                /**
477                 * Max amount of time to wait for responses.
478                 * <p>By default 5 seconds.
479                 * @param timeout the response timeout value
480                 */
481                Builder responseTimeout(Duration timeout);
482
483                /**
484                 * Apply the given configurer to this builder instance.
485                 * <p>This can be useful for applying pre-packaged customizations.
486                 * @param configurer the configurer to apply
487                 */
488                Builder apply(WebTestClientConfigurer configurer);
489
490                /**
491                 * Build the {@link WebTestClient} instance.
492                 */
493                WebTestClient build();
494        }
495
496
497        /**
498         * Specification for providing the URI of a request.
499         *
500         * @param <S> a self reference to the spec type
501         */
502        interface UriSpec<S extends RequestHeadersSpec<?>> {
503
504                /**
505                 * Specify the URI using an absolute, fully constructed {@link URI}.
506                 * @return spec to add headers or perform the exchange
507                 */
508                S uri(URI uri);
509
510                /**
511                 * Specify the URI for the request using a URI template and URI variables.
512                 * If a {@link UriBuilderFactory} was configured for the client (e.g.
513                 * with a base URI) it will be used to expand the URI template.
514                 * @return spec to add headers or perform the exchange
515                 */
516                S uri(String uri, Object... uriVariables);
517
518                /**
519                 * Specify the URI for the request using a URI template and URI variables.
520                 * If a {@link UriBuilderFactory} was configured for the client (e.g.
521                 * with a base URI) it will be used to expand the URI template.
522                 * @return spec to add headers or perform the exchange
523                 */
524                S uri(String uri, Map<String, ?> uriVariables);
525
526                /**
527                 * Build the URI for the request with a {@link UriBuilder} obtained
528                 * through the {@link UriBuilderFactory} configured for this client.
529                 * @return spec to add headers or perform the exchange
530                 */
531                S uri(Function<UriBuilder, URI> uriFunction);
532        }
533
534
535        /**
536         * Specification for adding request headers and performing an exchange.
537         *
538         * @param <S> a self reference to the spec type
539         */
540        interface RequestHeadersSpec<S extends RequestHeadersSpec<S>> {
541
542                /**
543                 * Set the list of acceptable {@linkplain MediaType media types}, as
544                 * specified by the {@code Accept} header.
545                 * @param acceptableMediaTypes the acceptable media types
546                 * @return the same instance
547                 */
548                S accept(MediaType... acceptableMediaTypes);
549
550                /**
551                 * Set the list of acceptable {@linkplain Charset charsets}, as specified
552                 * by the {@code Accept-Charset} header.
553                 * @param acceptableCharsets the acceptable charsets
554                 * @return the same instance
555                 */
556                S acceptCharset(Charset... acceptableCharsets);
557
558                /**
559                 * Add a cookie with the given name and value.
560                 * @param name the cookie name
561                 * @param value the cookie value
562                 * @return the same instance
563                 */
564                S cookie(String name, String value);
565
566                /**
567                 * Manipulate this request's cookies with the given consumer. The
568                 * map provided to the consumer is "live", so that the consumer can be used to
569                 * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing header values,
570                 * {@linkplain MultiValueMap#remove(Object) remove} values, or use any of the other
571                 * {@link MultiValueMap} methods.
572                 * @param cookiesConsumer a function that consumes the cookies map
573                 * @return this builder
574                 */
575                S cookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);
576
577                /**
578                 * Set the value of the {@code If-Modified-Since} header.
579                 * <p>The date should be specified as the number of milliseconds since
580                 * January 1, 1970 GMT.
581                 * @param ifModifiedSince the new value of the header
582                 * @return the same instance
583                 */
584                S ifModifiedSince(ZonedDateTime ifModifiedSince);
585
586                /**
587                 * Set the values of the {@code If-None-Match} header.
588                 * @param ifNoneMatches the new value of the header
589                 * @return the same instance
590                 */
591                S ifNoneMatch(String... ifNoneMatches);
592
593                /**
594                 * Add the given, single header value under the given name.
595                 * @param headerName  the header name
596                 * @param headerValues the header value(s)
597                 * @return the same instance
598                 */
599                S header(String headerName, String... headerValues);
600
601                /**
602                 * Manipulate the request's headers with the given consumer. The
603                 * headers provided to the consumer are "live", so that the consumer can be used to
604                 * {@linkplain HttpHeaders#set(String, String) overwrite} existing header values,
605                 * {@linkplain HttpHeaders#remove(Object) remove} values, or use any of the other
606                 * {@link HttpHeaders} methods.
607                 * @param headersConsumer a function that consumes the {@code HttpHeaders}
608                 * @return this builder
609                 */
610                S headers(Consumer<HttpHeaders> headersConsumer);
611
612                /**
613                 * Set the attribute with the given name to the given value.
614                 * @param name the name of the attribute to add
615                 * @param value the value of the attribute to add
616                 * @return this builder
617                 */
618                S attribute(String name, Object value);
619
620                /**
621                 * Manipulate the request attributes with the given consumer. The attributes provided to
622                 * the consumer are "live", so that the consumer can be used to inspect attributes,
623                 * remove attributes, or use any of the other map-provided methods.
624                 * @param attributesConsumer a function that consumes the attributes
625                 * @return this builder
626                 */
627                S attributes(Consumer<Map<String, Object>> attributesConsumer);
628
629                /**
630                 * Perform the exchange without a request body.
631                 * @return spec for decoding the response
632                 */
633                ResponseSpec exchange();
634        }
635
636
637        /**
638         * Specification for providing body of a request.
639         */
640        interface RequestBodySpec extends RequestHeadersSpec<RequestBodySpec> {
641                /**
642                 * Set the length of the body in bytes, as specified by the
643                 * {@code Content-Length} header.
644                 * @param contentLength the content length
645                 * @return the same instance
646                 * @see HttpHeaders#setContentLength(long)
647                 */
648                RequestBodySpec contentLength(long contentLength);
649
650                /**
651                 * Set the {@linkplain MediaType media type} of the body, as specified
652                 * by the {@code Content-Type} header.
653                 * @param contentType the content type
654                 * @return the same instance
655                 * @see HttpHeaders#setContentType(MediaType)
656                 */
657                RequestBodySpec contentType(MediaType contentType);
658
659                /**
660                 * Set the body to the given {@code Object} value. This method invokes the
661                 * {@link org.springframework.web.reactive.function.client.WebClient.RequestBodySpec#bodyValue(Object)
662                 * bodyValue} method on the underlying {@code WebClient}.
663                 * @param body the value to write to the request body
664                 * @return spec for further declaration of the request
665                 * @since 5.2
666                 */
667                RequestHeadersSpec<?> bodyValue(Object body);
668
669                /**
670                 * Set the body from the given {@code Publisher}. Shortcut for
671                 * {@link #body(BodyInserter)} with a
672                 * {@linkplain BodyInserters#fromPublisher Publisher inserter}.
673                 * @param publisher the request body data
674                 * @param elementClass the class of elements contained in the publisher
675                 * @param <T> the type of the elements contained in the publisher
676                 * @param <S> the type of the {@code Publisher}
677                 * @return spec for further declaration of the request
678                 */
679                <T, S extends Publisher<T>> RequestHeadersSpec<?> body(S publisher, Class<T> elementClass);
680
681                /**
682                 * Variant of {@link #body(Publisher, Class)} that allows providing
683                 * element type information with generics.
684                 * @param publisher the request body data
685                 * @param elementTypeRef the type reference of elements contained in the publisher
686                 * @param <T> the type of the elements contained in the publisher
687                 * @param <S> the type of the {@code Publisher}
688                 * @return spec for further declaration of the request
689                 * @since 5.2
690                 */
691                <T, S extends Publisher<T>> RequestHeadersSpec<?> body(
692                                S publisher, ParameterizedTypeReference<T> elementTypeRef);
693
694                /**
695                 * Set the body from the given producer. This method invokes the
696                 * {@link org.springframework.web.reactive.function.client.WebClient.RequestBodySpec#body(Object, Class)
697                 * body(Object, Class)} method on the underlying {@code WebClient}.
698                 * @param producer the producer to write to the request. This must be a
699                 * {@link Publisher} or another producer adaptable to a
700                 * {@code Publisher} via {@link ReactiveAdapterRegistry}
701                 * @param elementClass the class of elements contained in the producer
702                 * @return spec for further declaration of the request
703                 * @since 5.2
704                 */
705                RequestHeadersSpec<?> body(Object producer, Class<?> elementClass);
706
707                /**
708                 * Set the body from the given producer. This method invokes the
709                 * {@link org.springframework.web.reactive.function.client.WebClient.RequestBodySpec#body(Object, ParameterizedTypeReference)
710                 * body(Object, ParameterizedTypeReference)} method on the underlying {@code WebClient}.
711                 * @param producer the producer to write to the request. This must be a
712                 * {@link Publisher} or another producer adaptable to a
713                 * {@code Publisher} via {@link ReactiveAdapterRegistry}
714                 * @param elementTypeRef the type reference of elements contained in the producer
715                 * @return spec for further declaration of the request
716                 * @since 5.2
717                 */
718                RequestHeadersSpec<?> body(Object producer, ParameterizedTypeReference<?> elementTypeRef);
719
720                /**
721                 * Set the body of the request to the given {@code BodyInserter}.
722                 * This method invokes the
723                 * {@link org.springframework.web.reactive.function.client.WebClient.RequestBodySpec#body(BodyInserter)
724                 * body(BodyInserter)} method on the underlying {@code WebClient}.
725                 * @param inserter the body inserter to use
726                 * @return spec for further declaration of the request
727                 * @see org.springframework.web.reactive.function.BodyInserters
728                 */
729                RequestHeadersSpec<?> body(BodyInserter<?, ? super ClientHttpRequest> inserter);
730
731                /**
732                 * Shortcut for {@link #body(BodyInserter)} with a
733                 * {@linkplain BodyInserters#fromValue value inserter}.
734                 * As of 5.2 this method delegates to {@link #bodyValue(Object)}.
735                 * @deprecated as of Spring Framework 5.2 in favor of {@link #bodyValue(Object)}
736                 */
737                @Deprecated
738                RequestHeadersSpec<?> syncBody(Object body);
739        }
740
741
742        /**
743         * Specification for providing request headers and the URI of a request.
744         *
745         * @param <S> a self reference to the spec type
746         */
747        interface RequestHeadersUriSpec<S extends RequestHeadersSpec<S>> extends UriSpec<S>, RequestHeadersSpec<S> {
748        }
749
750        /**
751         * Specification for providing the body and the URI of a request.
752         */
753        interface RequestBodyUriSpec extends RequestBodySpec, RequestHeadersUriSpec<RequestBodySpec> {
754        }
755
756
757        /**
758         * Chained API for applying assertions to a response.
759         */
760        interface ResponseSpec {
761
762                /**
763                 * Assertions on the response status.
764                 */
765                StatusAssertions expectStatus();
766
767                /**
768                 * Assertions on the headers of the response.
769                 */
770                HeaderAssertions expectHeader();
771
772                /**
773                 * Consume and decode the response body to a single object of type
774                 * {@code <B>} and then apply assertions.
775                 * @param bodyType the expected body type
776                 */
777                <B> BodySpec<B, ?> expectBody(Class<B> bodyType);
778
779                /**
780                 * Alternative to {@link #expectBody(Class)} that accepts information
781                 * about a target type with generics.
782                 */
783                <B> BodySpec<B, ?> expectBody(ParameterizedTypeReference<B> bodyType);
784
785                /**
786                 * Consume and decode the response body to {@code List<E>} and then apply
787                 * List-specific assertions.
788                 * @param elementType the expected List element type
789                 */
790                <E> ListBodySpec<E> expectBodyList(Class<E> elementType);
791
792                /**
793                 * Alternative to {@link #expectBodyList(Class)} that accepts information
794                 * about a target type with generics.
795                 */
796                <E> ListBodySpec<E> expectBodyList(ParameterizedTypeReference<E> elementType);
797
798                /**
799                 * Consume and decode the response body to {@code byte[]} and then apply
800                 * assertions on the raw content (e.g. isEmpty, JSONPath, etc.)
801                 */
802                BodyContentSpec expectBody();
803
804                /**
805                 * Exit the chained API and consume the response body externally. This
806                 * is useful for testing infinite streams (e.g. SSE) where you need to
807                 * to assert decoded objects as they come and then cancel at some point
808                 * when test objectives are met. Consider using {@code StepVerifier}
809                 * from {@literal "reactor-test"} to assert the {@code Flux<T>} stream
810                 * of decoded objects.
811                 *
812                 * <p><strong>Note:</strong> Do not use this option for cases where there
813                 * is no content (e.g. 204, 4xx) or you're not interested in the content.
814                 * For such cases you can use {@code expectBody().isEmpty()} or
815                 * {@code expectBody(Void.class)} which ensures that resources are
816                 * released regardless of whether the response has content or not.
817                 */
818                <T> FluxExchangeResult<T> returnResult(Class<T> elementClass);
819
820                /**
821                 * Alternative to {@link #returnResult(Class)} that accepts information
822                 * about a target type with generics.
823                 */
824                <T> FluxExchangeResult<T> returnResult(ParameterizedTypeReference<T> elementTypeRef);
825        }
826
827
828        /**
829         * Spec for expectations on the response body decoded to a single Object.
830         *
831         * @param <S> a self reference to the spec type
832         * @param <B> the body type
833         */
834        interface BodySpec<B, S extends BodySpec<B, S>> {
835
836                /**
837                 * Assert the extracted body is equal to the given value.
838                 */
839                <T extends S> T isEqualTo(B expected);
840
841                /**
842                 * Assert the extracted body with a {@link Matcher}.
843                 * @since 5.1
844                 */
845                <T extends S> T value(Matcher<B> matcher);
846
847                /**
848                 * Transform the extracted the body with a function, e.g. extracting a
849                 * property, and assert the mapped value with a {@link Matcher}.
850                 * @since 5.1
851                 */
852                <T extends S, R> T value(Function<B, R> bodyMapper, Matcher<R> matcher);
853
854                /**
855                 * Assert the extracted body with a {@link Consumer}.
856                 * @since 5.1
857                 */
858                <T extends S> T value(Consumer<B> consumer);
859
860                /**
861                 * Assert the exchange result with the given {@link Consumer}.
862                 */
863                <T extends S> T consumeWith(Consumer<EntityExchangeResult<B>> consumer);
864
865                /**
866                 * Exit the chained API and return an {@code ExchangeResult} with the
867                 * decoded response content.
868                 */
869                EntityExchangeResult<B> returnResult();
870        }
871
872
873        /**
874         * Spec for expectations on the response body decoded to a List.
875         *
876         * @param <E> the body list element type
877         */
878        interface ListBodySpec<E> extends BodySpec<List<E>, ListBodySpec<E>> {
879
880                /**
881                 * Assert the extracted list of values is of the given size.
882                 * @param size the expected size
883                 */
884                ListBodySpec<E> hasSize(int size);
885
886                /**
887                 * Assert the extracted list of values contains the given elements.
888                 * @param elements the elements to check
889                 */
890                @SuppressWarnings("unchecked")
891                ListBodySpec<E> contains(E... elements);
892
893                /**
894                 * Assert the extracted list of values doesn't contain the given elements.
895                 * @param elements the elements to check
896                 */
897                @SuppressWarnings("unchecked")
898                ListBodySpec<E> doesNotContain(E... elements);
899        }
900
901
902        /**
903         * Spec for expectations on the response body content.
904         */
905        interface BodyContentSpec {
906
907                /**
908                 * Assert the response body is empty and return the exchange result.
909                 */
910                EntityExchangeResult<Void> isEmpty();
911
912                /**
913                 * Parse the expected and actual response content as JSON and perform a
914                 * "lenient" comparison verifying the same attribute-value pairs.
915                 * <p>Use of this option requires the
916                 * <a href="https://jsonassert.skyscreamer.org/">JSONassert</a> library
917                 * on to be on the classpath.
918                 * @param expectedJson the expected JSON content.
919                 */
920                BodyContentSpec json(String expectedJson);
921
922                /**
923                 * Parse expected and actual response content as XML and assert that
924                 * the two are "similar", i.e. they contain the same elements and
925                 * attributes regardless of order.
926                 * <p>Use of this method requires the
927                 * <a href="https://github.com/xmlunit/xmlunit">XMLUnit</a> library on
928                 * the classpath.
929                 * @param expectedXml the expected JSON content.
930                 * @since 5.1
931                 * @see org.springframework.test.util.XmlExpectationsHelper#assertXmlEqual(String, String)
932                 */
933                BodyContentSpec xml(String expectedXml);
934
935                /**
936                 * Access to response body assertions using a
937                 * <a href="https://github.com/jayway/JsonPath">JsonPath</a> expression
938                 * to inspect a specific subset of the body.
939                 * <p>The JSON path expression can be a parameterized string using
940                 * formatting specifiers as defined in {@link String#format}.
941                 * @param expression the JsonPath expression
942                 * @param args arguments to parameterize the expression
943                 */
944                JsonPathAssertions jsonPath(String expression, Object... args);
945
946                /**
947                 * Access to response body assertions using an XPath expression to
948                 * inspect a specific subset of the body.
949                 * <p>The XPath expression can be a parameterized string using
950                 * formatting specifiers as defined in {@link String#format}.
951                 * @param expression the XPath expression
952                 * @param args arguments to parameterize the expression
953                 * @since 5.1
954                 * @see #xpath(String, Map, Object...)
955                 */
956                default XpathAssertions xpath(String expression, Object... args) {
957                        return xpath(expression, null, args);
958                }
959
960                /**
961                 * Access to response body assertions with specific namespaces using an
962                 * XPath expression to inspect a specific subset of the body.
963                 * <p>The XPath expression can be a parameterized string using
964                 * formatting specifiers as defined in {@link String#format}.
965                 * @param expression the XPath expression
966                 * @param namespaces the namespaces to use
967                 * @param args arguments to parameterize the expression
968                 * @since 5.1
969                 */
970                XpathAssertions xpath(String expression, @Nullable Map<String, String> namespaces, Object... args);
971
972                /**
973                 * Assert the response body content with the given {@link Consumer}.
974                 * @param consumer the consumer for the response body; the input
975                 * {@code byte[]} may be {@code null} if there was no response body.
976                 */
977                BodyContentSpec consumeWith(Consumer<EntityExchangeResult<byte[]>> consumer);
978
979                /**
980                 * Exit the chained API and return an {@code ExchangeResult} with the
981                 * raw response content.
982                 */
983                EntityExchangeResult<byte[]> returnResult();
984        }
985
986}