diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfiguration.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfiguration.java index 7d7a7ba769c..a4184e49cef 100644 --- a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfiguration.java +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfiguration.java @@ -16,12 +16,19 @@ package org.springframework.boot.test.autoconfigure.web.reactive; +import java.util.Collection; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration; +import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.util.CollectionUtils; +import org.springframework.web.reactive.function.client.ExchangeStrategies; import org.springframework.web.reactive.function.client.WebClient; /** @@ -31,13 +38,32 @@ import org.springframework.web.reactive.function.client.WebClient; * @since 2.0.0 */ @Configuration -@ConditionalOnClass({ WebClient.class, WebTestClient.class }) +@ConditionalOnClass({WebClient.class, WebTestClient.class}) +@AutoConfigureAfter(CodecsAutoConfiguration.class) public class WebTestClientAutoConfiguration { @Bean @ConditionalOnMissingBean public WebTestClient webTestClient(ApplicationContext applicationContext) { - return WebTestClient.bindToApplicationContext(applicationContext).build(); + + WebTestClient.Builder clientBuilder = WebTestClient + .bindToApplicationContext(applicationContext).configureClient(); + customizeWebTestClientCodecs(clientBuilder, applicationContext); + return clientBuilder.build(); + } + + private void customizeWebTestClientCodecs(WebTestClient.Builder clientBuilder, + ApplicationContext applicationContext) { + + Collection codecCustomizers = applicationContext + .getBeansOfType(CodecCustomizer.class).values(); + if (!CollectionUtils.isEmpty(codecCustomizers)) { + clientBuilder.exchangeStrategies(ExchangeStrategies.builder() + .codecs(codecs -> { + codecCustomizers.forEach(codecCustomizer -> codecCustomizer.customize(codecs)); + }) + .build()); + } } } diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfigurationTests.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfigurationTests.java new file mode 100644 index 00000000000..75359dcf762 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfigurationTests.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.test.autoconfigure.web.reactive; + +import org.junit.After; +import org.junit.Test; + +import org.springframework.boot.web.codec.CodecCustomizer; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.http.codec.CodecConfigurer; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.server.WebHandler; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link WebTestClientAutoConfiguration} + * + * @author Brian Clozel + */ +public class WebTestClientAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void shouldCustomizeClientCodecs() throws Exception { + load(CodecConfiguration.class); + WebTestClient webTestClient = this.context.getBean(WebTestClient.class); + CodecCustomizer codecCustomizer = this.context.getBean(CodecCustomizer.class); + assertThat(webTestClient).isNotNull(); + verify(codecCustomizer).customize(any(CodecConfigurer.class)); + } + + + private void load(Class... config) { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(config); + ctx.register(WebTestClientAutoConfiguration.class); + ctx.refresh(); + this.context = ctx; + } + + @Configuration + static class BaseConfiguration { + + @Bean + public WebHandler webHandler() { + return mock(WebHandler.class); + } + } + + @Configuration + @Import(BaseConfiguration.class) + static class CodecConfiguration { + + @Bean + public CodecCustomizer myCodecCustomizer() { + return mock(CodecCustomizer.class); + } + + } + +} diff --git a/spring-boot-test/pom.xml b/spring-boot-test/pom.xml index e0308842c89..e4011bd9c46 100644 --- a/spring-boot-test/pom.xml +++ b/spring-boot-test/pom.xml @@ -105,6 +105,11 @@ spring-web true + + org.springframework + spring-webflux + true + net.sourceforge.htmlunit htmlunit @@ -162,11 +167,6 @@ spring-webmvc test - - org.springframework - spring-webflux - test - diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizer.java b/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizer.java index e0a6b71108a..ac4c1fc2502 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizer.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizer.java @@ -16,6 +16,8 @@ package org.springframework.boot.test.web.reactive; +import java.util.Collection; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -23,6 +25,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -31,6 +34,8 @@ import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.util.CollectionUtils; +import org.springframework.web.reactive.function.client.ExchangeStrategies; /** * {@link ContextCustomizer} for {@link WebTestClient}. @@ -115,7 +120,9 @@ class WebTestClientContextCustomizer implements ContextCustomizer { String port = this.applicationContext.getEnvironment() .getProperty("local.server.port", "8080"); String baseUrl = (sslEnabled ? "https" : "http") + "://localhost:" + port; - return WebTestClient.bindToServer().baseUrl(baseUrl).build(); + WebTestClient.Builder builder = WebTestClient.bindToServer(); + customizeWebTestClientCodecs(builder, this.applicationContext); + return builder.baseUrl(baseUrl).build(); } private boolean isSslEnabled(ApplicationContext context) { @@ -130,6 +137,18 @@ class WebTestClientContextCustomizer implements ContextCustomizer { } } + private void customizeWebTestClientCodecs(WebTestClient.Builder clientBuilder, + ApplicationContext context) { + Collection codecCustomizers = context.getBeansOfType(CodecCustomizer.class).values(); + if (!CollectionUtils.isEmpty(codecCustomizers)) { + clientBuilder.exchangeStrategies(ExchangeStrategies.builder() + .codecs(codecs -> { + codecCustomizers.forEach(codecCustomizer -> codecCustomizer.customize(codecs)); + }) + .build()); + } + } + } }