diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java index 66464087484..76c6edecdf3 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java @@ -68,7 +68,7 @@ * @author MichaƂ Rowicki * @since 5.0 */ -class DefaultWebTestClient implements WebTestClient { +public class DefaultWebTestClient implements WebTestClient { private final WiretapConnector wiretapConnector; @@ -91,7 +91,7 @@ class DefaultWebTestClient implements WebTestClient { private final AtomicLong requestIndex = new AtomicLong(); - DefaultWebTestClient(ClientHttpConnector connector, + protected DefaultWebTestClient(ClientHttpConnector connector, Function exchangeFactory, UriBuilderFactory uriBuilderFactory, @Nullable HttpHeaders headers, @Nullable MultiValueMap cookies, Consumer> entityResultConsumer, diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java index f3c9e3650af..fe3ab134f4e 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java @@ -51,7 +51,7 @@ * @author Rossen Stoyanchev * @since 5.0 */ -class DefaultWebTestClientBuilder implements WebTestClient.Builder { +public class DefaultWebTestClientBuilder implements WebTestClient.Builder { private static final boolean reactorNettyClientPresent; @@ -77,54 +77,54 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder { @Nullable - private final WebHttpHandlerBuilder httpHandlerBuilder; + protected final WebHttpHandlerBuilder httpHandlerBuilder; @Nullable - private final ClientHttpConnector connector; + protected final ClientHttpConnector connector; @Nullable - private String baseUrl; + protected String baseUrl; @Nullable - private UriBuilderFactory uriBuilderFactory; + protected UriBuilderFactory uriBuilderFactory; @Nullable - private HttpHeaders defaultHeaders; + protected HttpHeaders defaultHeaders; @Nullable - private MultiValueMap defaultCookies; + protected MultiValueMap defaultCookies; @Nullable - private List filters; + protected List filters; - private Consumer> entityResultConsumer = result -> {}; + protected Consumer> entityResultConsumer = result -> {}; @Nullable - private ExchangeStrategies strategies; + protected ExchangeStrategies strategies; @Nullable - private List> strategiesConfigurers; + protected List> strategiesConfigurers; @Nullable - private Duration responseTimeout; + protected Duration responseTimeout; /** Determine connector via classpath detection. */ - DefaultWebTestClientBuilder() { + protected DefaultWebTestClientBuilder() { this(null, null); } /** Use HttpHandlerConnector with mock server. */ - DefaultWebTestClientBuilder(WebHttpHandlerBuilder httpHandlerBuilder) { + protected DefaultWebTestClientBuilder(WebHttpHandlerBuilder httpHandlerBuilder) { this(httpHandlerBuilder, null); } /** Use given connector. */ - DefaultWebTestClientBuilder(ClientHttpConnector connector) { + protected DefaultWebTestClientBuilder(ClientHttpConnector connector) { this(null, connector); } - DefaultWebTestClientBuilder( + protected DefaultWebTestClientBuilder( @Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) { Assert.isTrue(httpHandlerBuilder == null || connector == null, @@ -139,7 +139,7 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder { } /** Copy constructor. */ - DefaultWebTestClientBuilder(DefaultWebTestClientBuilder other) { + protected DefaultWebTestClientBuilder(DefaultWebTestClientBuilder other) { this.httpHandlerBuilder = (other.httpHandlerBuilder != null ? other.httpHandlerBuilder.clone() : null); this.connector = other.connector; this.responseTimeout = other.responseTimeout; @@ -323,7 +323,7 @@ else if (httpComponentsClientPresent) { } } - private ExchangeStrategies initExchangeStrategies() { + protected ExchangeStrategies initExchangeStrategies() { if (CollectionUtils.isEmpty(this.strategiesConfigurers)) { return (this.strategies != null ? this.strategies : ExchangeStrategies.withDefaults()); } @@ -333,7 +333,7 @@ private ExchangeStrategies initExchangeStrategies() { return builder.build(); } - private UriBuilderFactory initUriBuilderFactory() { + protected UriBuilderFactory initUriBuilderFactory() { if (this.uriBuilderFactory != null) { return this.uriBuilderFactory; } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/client/AbstractMockMvcServerSpec.java b/spring-test/src/main/java/org/springframework/test/web/servlet/client/AbstractMockMvcServerSpec.java index 02c24c01dec..d80334dee48 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/client/AbstractMockMvcServerSpec.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/client/AbstractMockMvcServerSpec.java @@ -18,8 +18,6 @@ import jakarta.servlet.Filter; -import org.springframework.http.client.reactive.ClientHttpConnector; -import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.test.web.servlet.DispatcherServletCustomizer; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.RequestBuilder; @@ -93,14 +91,13 @@ private T self() { protected abstract ConfigurableMockMvcBuilder getMockMvcBuilder(); @Override - public WebTestClient.Builder configureClient() { + public MockMvcWebTestClient.Builder configureClient() { MockMvc mockMvc = getMockMvcBuilder().build(); - ClientHttpConnector connector = new MockMvcHttpConnector(mockMvc); - return WebTestClient.bindToServer(connector); + return MockMvcWebTestClient.bindToMockMvc(mockMvc); } @Override - public WebTestClient build() { + public MockMvcWebTestClient build() { return configureClient().build(); } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/client/DefaultMockMvcWebTestClient.java b/spring-test/src/main/java/org/springframework/test/web/servlet/client/DefaultMockMvcWebTestClient.java new file mode 100644 index 00000000000..4f6adcd301c --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/client/DefaultMockMvcWebTestClient.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.web.servlet.client; + +import java.time.Duration; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.test.web.reactive.server.DefaultWebTestClient; +import org.springframework.test.web.reactive.server.EntityExchangeResult; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.client.ExchangeFunction; +import org.springframework.web.util.UriBuilderFactory; + +/** + * Default implementation of {@link MockMvcWebTestClient}. + * + * @author Justin Tay + */ +public class DefaultMockMvcWebTestClient extends DefaultWebTestClient implements MockMvcWebTestClient { + private final DefaultMockMvcWebTestClientBuilder builder; + + DefaultMockMvcWebTestClient(ClientHttpConnector connector, + Function exchangeFactory, UriBuilderFactory uriBuilderFactory, + HttpHeaders headers, MultiValueMap cookies, + Consumer> entityResultConsumer, Duration responseTimeout, + DefaultMockMvcWebTestClientBuilder clientBuilder) { + super(connector, exchangeFactory, uriBuilderFactory, headers, cookies, entityResultConsumer, responseTimeout, + clientBuilder); + this.builder = clientBuilder; + } + + @Override + public MockMvcWebTestClient.Builder mutate() { + return new DefaultMockMvcWebTestClientBuilder(this.builder); + } + + @Override + public MockMvcWebTestClient mutateWith(RequestPostProcessor requestPostProcessor) { + return mutate().requestPostProcessor(requestPostProcessor).build(); + } + + @Override + public MockMvcWebTestClient mutateWith(MockMvcWebTestClientConfigurer configurer) { + return mutate().apply(configurer).build(); + } +} diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/client/DefaultMockMvcWebTestClientBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/client/DefaultMockMvcWebTestClientBuilder.java new file mode 100644 index 00000000000..65e257aafd1 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/client/DefaultMockMvcWebTestClientBuilder.java @@ -0,0 +1,221 @@ +/* + * Copyright 2002-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.web.servlet.client; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.http.codec.ClientCodecConfigurer; +import org.springframework.test.web.reactive.server.DefaultWebTestClientBuilder; +import org.springframework.test.web.reactive.server.EntityExchangeResult; +import org.springframework.test.web.reactive.server.WebTestClientConfigurer; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.client.MockMvcWebTestClient.Builder; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.util.CollectionUtils; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.ExchangeFunction; +import org.springframework.web.reactive.function.client.ExchangeFunctions; +import org.springframework.web.reactive.function.client.ExchangeStrategies; +import org.springframework.web.util.UriBuilderFactory; + +/** + * Default implementation of {@link MockMvcWebTestClient.Builder}. + * + * @author Justin Tay + */ +public class DefaultMockMvcWebTestClientBuilder extends DefaultWebTestClientBuilder implements MockMvcWebTestClient.Builder { + + private MockMvc mockMvc; + private List requestPostProcessors = new ArrayList<>(); + private List> requestBuilderCustomizers = new ArrayList<>(); + + DefaultMockMvcWebTestClientBuilder(MockMvc mockMvc) { + this.mockMvc = mockMvc; + } + + /** Copy constructor. */ + DefaultMockMvcWebTestClientBuilder(DefaultMockMvcWebTestClientBuilder other) { + super(other); + this.mockMvc = other.mockMvc; + this.requestPostProcessors = new ArrayList<>(other.requestPostProcessors); + this.requestBuilderCustomizers = new ArrayList<>(other.requestBuilderCustomizers); + } + + @Override + public Builder mockMvc(MockMvc mockMvc) { + this.mockMvc = mockMvc; + return this; + } + + @Override + public Builder requestPostProcessors(Consumer> requestPostProcessorsConsumer) { + requestPostProcessorsConsumer.accept(this.requestPostProcessors); + return this; + } + + @Override + public Builder requestPostProcessor(RequestPostProcessor requestPostProcessor) { + this.requestPostProcessors.add(requestPostProcessor); + return this; + } + + @Override + public Builder requestBuilderCustomizer(Consumer requestBuilderCustomizer) { + this.requestBuilderCustomizers.add(requestBuilderCustomizer); + return this; + } + + @Override + public Builder requestBuilderCustomizers(Consumer>> requestBuilderCustomizersConsumer) { + requestBuilderCustomizersConsumer.accept(this.requestBuilderCustomizers); + return this; + } + + @Override + public Builder apply(MockMvcWebTestClientConfigurer configurer) { + configurer.afterConfigurerAdded(this, this.httpHandlerBuilder, this.connector); + return this; + } + + @Override + public MockMvcWebTestClient build() { + ClientHttpConnector connectorToUse = new MockMvcHttpConnector(this.mockMvc, customizer -> { + this.requestPostProcessors.forEach(customizer::with); + this.requestBuilderCustomizers.forEach(builderCustomizer -> builderCustomizer.accept(customizer)); + }); + Function exchangeFactory = connector -> { + ExchangeFunction exchange = ExchangeFunctions.create(connector, initExchangeStrategies()); + if (CollectionUtils.isEmpty(this.filters)) { + return exchange; + } + return this.filters.stream() + .reduce(ExchangeFilterFunction::andThen) + .map(filter -> filter.apply(exchange)) + .orElse(exchange); + + }; + return new DefaultMockMvcWebTestClient(connectorToUse, exchangeFactory, initUriBuilderFactory(), + this.defaultHeaders != null ? HttpHeaders.readOnlyHttpHeaders(this.defaultHeaders) : null, + this.defaultCookies != null ? CollectionUtils.unmodifiableMultiValueMap(this.defaultCookies) : null, + this.entityResultConsumer, this.responseTimeout, new DefaultMockMvcWebTestClientBuilder(this)); + } + + /* Override methods to return covariant return type */ + @Override + public Builder baseUrl(String baseUrl) { + super.baseUrl(baseUrl); + return this; + } + + @Override + public Builder uriBuilderFactory( + UriBuilderFactory uriBuilderFactory) { + super.uriBuilderFactory(uriBuilderFactory); + return this; + } + + @Override + public Builder defaultHeader(String header, + String... values) { + super.defaultHeader(header, values); + return this; + } + + @Override + public Builder defaultHeaders( + Consumer headersConsumer) { + super.defaultHeaders(headersConsumer); + return this; + } + + @Override + public Builder defaultCookie(String cookie, + String... values) { + super.defaultCookie(cookie, values); + return this; + } + + @Override + public Builder defaultCookies( + Consumer> cookiesConsumer) { + super.defaultCookies(cookiesConsumer); + return this; + } + + @Override + public Builder filter(ExchangeFilterFunction filter) { + super.filter(filter); + return this; + } + + @Override + public Builder filters( + Consumer> filtersConsumer) { + super.filters(filtersConsumer); + return this; + } + + @Override + public Builder entityExchangeResultConsumer( + Consumer> entityResultConsumer) { + super.entityExchangeResultConsumer(entityResultConsumer); + return this; + } + + @Override + public Builder codecs( + Consumer configurer) { + super.codecs(configurer); + return this; + } + + @Override + public Builder exchangeStrategies( + ExchangeStrategies strategies) { + super.exchangeStrategies(strategies); + return this; + } + + @Override + @SuppressWarnings("deprecation") + public Builder exchangeStrategies( + Consumer configurer) { + super.exchangeStrategies(configurer); + return this; + } + + @Override + public Builder apply( + WebTestClientConfigurer configurer) { + super.apply(configurer); + return this; + } + + @Override + public Builder responseTimeout(Duration timeout) { + super.responseTimeout(timeout); + return this; + } +} diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcHttpConnector.java b/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcHttpConnector.java index adcb0115cfd..42c07b98bfe 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcHttpConnector.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcHttpConnector.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.function.Function; import jakarta.servlet.http.Cookie; @@ -82,12 +83,13 @@ public class MockMvcHttpConnector implements ClientHttpConnector { private final MockMvc mockMvc; + private final Consumer requestBuilderCustomizer; - public MockMvcHttpConnector(MockMvc mockMvc) { + public MockMvcHttpConnector(MockMvc mockMvc, Consumer requestBuilderCustomizer) { this.mockMvc = mockMvc; + this.requestBuilderCustomizer = requestBuilderCustomizer; } - @Override public Mono connect( HttpMethod method, URI uri, Function> requestCallback) { @@ -147,6 +149,9 @@ private MockHttpServletRequestBuilder initRequestBuilder( if (!ObjectUtils.isEmpty(bytes)) { requestBuilder.content(bytes); } + if(this.requestBuilderCustomizer != null) { + this.requestBuilderCustomizer.accept(requestBuilder); + } return requestBuilder; } @@ -175,6 +180,9 @@ private MockHttpServletRequestBuilder initRequestBuilder( })) .blockLast(TIMEOUT); + if(this.requestBuilderCustomizer != null) { + this.requestBuilderCustomizer.accept(requestBuilder); + } return requestBuilder; } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClient.java b/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClient.java index 82ad8587097..021037231ac 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClient.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClient.java @@ -16,16 +16,22 @@ package org.springframework.test.web.servlet.client; +import java.time.Duration; +import java.util.List; +import java.util.function.Consumer; import java.util.function.Supplier; import jakarta.servlet.Filter; import org.springframework.format.support.FormattingConversionService; -import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.http.HttpHeaders; +import org.springframework.http.codec.ClientCodecConfigurer; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.lang.Nullable; +import org.springframework.test.web.reactive.server.EntityExchangeResult; import org.springframework.test.web.reactive.server.ExchangeResult; import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.test.web.reactive.server.WebTestClientConfigurer; import org.springframework.test.web.servlet.DispatcherServletCustomizer; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -33,14 +39,20 @@ import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultHandler; import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; import org.springframework.test.web.servlet.setup.MockMvcConfigurer; import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder; +import org.springframework.util.MultiValueMap; import org.springframework.validation.Validator; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.ExchangeStrategies; +import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.servlet.FlashMapManager; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerInterceptor; @@ -48,6 +60,7 @@ import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import org.springframework.web.util.UriBuilderFactory; import org.springframework.web.util.pattern.PathPatternParser; /** @@ -57,7 +70,7 @@ *

Provides static factory methods and specs to initialize {@code MockMvc} * to which the {@code WebTestClient} connects to. For example: *

- * WebTestClient client = MockMvcWebTestClient.bindToController(myController)
+ * MockMvcWebTestClient client = MockMvcWebTestClient.bindToController(myController)
  *         .controllerAdvice(myControllerAdvice)
  *         .validator(myValidator)
  *         .build()
@@ -65,7 +78,7 @@
  *
  * 

The client itself can also be configured. For example: *

- * WebTestClient client = MockMvcWebTestClient.bindToController(myController)
+ * MockMvcWebTestClient client = MockMvcWebTestClient.bindToController(myController)
  *         .validator(myValidator)
  *         .configureClient()
  *         .baseUrl("/path")
@@ -75,7 +88,34 @@
  * @author Rossen Stoyanchev
  * @since 5.3
  */
-public interface MockMvcWebTestClient {
+public interface MockMvcWebTestClient extends WebTestClient {
+
+	/**
+	 * Return a builder to mutate properties of this web test client.
+	 */
+	Builder mutate();
+
+	/**
+	 * Mutate the {@link MockMvcWebTestClient}, apply the given requestPostProcessor, and build
+	 * a new instance. Essentially a shortcut for:
+	 * 
+	 * mutate().requestPostProcessor(requestPostProcessor).build();
+	 * 
+ * @param requestPostProcessor the requestPostProcessor to apply + * @return the mutated test client + */ + MockMvcWebTestClient mutateWith(RequestPostProcessor requestPostProcessor); + + /** + * Mutate the {@link MockMvcWebTestClient}, apply the given configurer, and build + * a new instance. Essentially a shortcut for: + *
+	 * mutate().apply(configurer).build();
+	 * 
+ * @param configurer the configurer to apply + * @return the mutated test client + */ + MockMvcWebTestClient mutateWith(MockMvcWebTestClientConfigurer configurer); /** * Begin creating a {@link WebTestClient} by providing the {@code @Controller} @@ -104,9 +144,17 @@ static MockMvcServerSpec bindToApplicationContext(WebApplicationContext conte * Begin creating a {@link WebTestClient} by providing an already * initialized {@link MockMvc} instance to use as the server. */ + @Deprecated // Changing the return type causes Spring Boot to throw in MockMvcAutoConfiguration static WebTestClient.Builder bindTo(MockMvc mockMvc) { - ClientHttpConnector connector = new MockMvcHttpConnector(mockMvc); - return WebTestClient.bindToServer(connector); + return bindToMockMvc(mockMvc); + } + + /** + * Begin creating a {@link WebTestClient} by providing an already + * initialized {@link MockMvc} instance to use as the server. + */ + static MockMvcWebTestClient.Builder bindToMockMvc(MockMvc mockMvc) { + return new DefaultMockMvcWebTestClientBuilder(mockMvc); } /** @@ -226,12 +274,12 @@ interface MockMvcServerSpec> { /** * Proceed to configure and build the test client. */ - WebTestClient.Builder configureClient(); + Builder configureClient(); /** * Shortcut to build the test client. */ - WebTestClient build(); + MockMvcWebTestClient build(); } @@ -381,4 +429,74 @@ ControllerSpec mappedInterceptors( ControllerSpec customHandlerMapping(Supplier factory); } + /** + * Steps for customizing the {@link WebClient} used to test with, + * internally delegating to a + * {@link org.springframework.web.reactive.function.client.WebClient.Builder + * WebClient.Builder}. + */ + interface Builder extends WebTestClient.Builder { + Builder mockMvc(MockMvc mockMvc); + + Builder requestPostProcessor(RequestPostProcessor requestPostProcessor); + + Builder requestPostProcessors(Consumer> requestPostProcessorsConsumer); + + Builder requestBuilderCustomizer(Consumer requestBuilderCustomizer); + + Builder requestBuilderCustomizers(Consumer>> requestBuilderCustomizersConsumer); + + Builder apply(MockMvcWebTestClientConfigurer configurer); + + /* Override methods to return covariant return type */ + @Override + Builder baseUrl(String baseUrl); + + @Override + Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory); + + @Override + Builder defaultHeader(String headerName, String... headerValues); + + @Override + Builder defaultHeaders(Consumer headersConsumer); + + @Override + Builder defaultCookie(String cookieName, String... cookieValues); + + @Override + Builder defaultCookies(Consumer> cookiesConsumer); + + @Override + Builder filter(ExchangeFilterFunction filter); + + @Override + Builder filters(Consumer> filtersConsumer); + + @Override + Builder entityExchangeResultConsumer(Consumer> consumer); + + @Override + Builder codecs(Consumer configurer); + + @Override + Builder exchangeStrategies(ExchangeStrategies strategies); + + @Override + @SuppressWarnings("deprecation") + Builder exchangeStrategies( + Consumer configurer); + + @Override + Builder responseTimeout(Duration timeout); + + @Override + Builder apply(WebTestClientConfigurer configurer); + + /** + * Build the {@link WebTestClient} instance. + */ + @Override + MockMvcWebTestClient build(); + } } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClientConfigurer.java b/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClientConfigurer.java new file mode 100644 index 00000000000..4633fc16cff --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClientConfigurer.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.web.servlet.client; + +import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.lang.Nullable; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; + +/** + * Contract that frameworks or applications can use to pre-package a set of + * customizations to a {@link MockMvcWebTestClient.Builder} and expose that + * as a shortcut. + * + * @author Rossen Stoyanchev + * @see MockServerConfigurer + */ +public interface MockMvcWebTestClientConfigurer { + + /** + * Invoked once only, immediately (i.e. before this method returns). + * @param builder the MockMvcWebTestClient builder to make changes to + * @param httpHandlerBuilder the builder for the "mock server" HttpHandler + * this client was configured for "mock server" testing + * @param connector the connector for "live" integration tests if this + * server was configured for live integration testing + */ + void afterConfigurerAdded(MockMvcWebTestClient.Builder builder, + @Nullable WebHttpHandlerBuilder httpHandlerBuilder, + @Nullable ClientHttpConnector connector); + +}