Commit f2e77e46 authored by Brian Clozel's avatar Brian Clozel

Auto-configure codecs in WebTestClient

This commit applies what's been done in gh-9166 for WebFlux client and
server, but for the `WebTestClient` auto-configuration.

`WebTestClient` can be configured for mock or integration tests and this
change applies `CodecCustomizer` beans to the client being built.

Closes gh-9577
parent 64777204
...@@ -16,12 +16,19 @@ ...@@ -16,12 +16,19 @@
package org.springframework.boot.test.autoconfigure.web.reactive; 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.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.test.web.reactive.server.WebTestClient; 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; import org.springframework.web.reactive.function.client.WebClient;
/** /**
...@@ -31,13 +38,32 @@ import org.springframework.web.reactive.function.client.WebClient; ...@@ -31,13 +38,32 @@ import org.springframework.web.reactive.function.client.WebClient;
* @since 2.0.0 * @since 2.0.0
*/ */
@Configuration @Configuration
@ConditionalOnClass({ WebClient.class, WebTestClient.class }) @ConditionalOnClass({WebClient.class, WebTestClient.class})
@AutoConfigureAfter(CodecsAutoConfiguration.class)
public class WebTestClientAutoConfiguration { public class WebTestClientAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public WebTestClient webTestClient(ApplicationContext applicationContext) { 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<CodecCustomizer> codecCustomizers = applicationContext
.getBeansOfType(CodecCustomizer.class).values();
if (!CollectionUtils.isEmpty(codecCustomizers)) {
clientBuilder.exchangeStrategies(ExchangeStrategies.builder()
.codecs(codecs -> {
codecCustomizers.forEach(codecCustomizer -> codecCustomizer.customize(codecs));
})
.build());
}
} }
} }
/*
* 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);
}
}
}
...@@ -105,6 +105,11 @@ ...@@ -105,6 +105,11 @@
<artifactId>spring-web</artifactId> <artifactId>spring-web</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>net.sourceforge.htmlunit</groupId> <groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId> <artifactId>htmlunit</artifactId>
...@@ -162,11 +167,6 @@ ...@@ -162,11 +167,6 @@
<artifactId>spring-webmvc</artifactId> <artifactId>spring-webmvc</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.test.web.reactive; package org.springframework.boot.test.web.reactive;
import java.util.Collection;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
...@@ -23,6 +25,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; ...@@ -23,6 +25,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.test.context.SpringBootTest; 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.boot.web.reactive.server.AbstractReactiveWebServerFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
...@@ -31,6 +34,8 @@ import org.springframework.core.annotation.AnnotatedElementUtils; ...@@ -31,6 +34,8 @@ import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient; 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}. * {@link ContextCustomizer} for {@link WebTestClient}.
...@@ -115,7 +120,9 @@ class WebTestClientContextCustomizer implements ContextCustomizer { ...@@ -115,7 +120,9 @@ class WebTestClientContextCustomizer implements ContextCustomizer {
String port = this.applicationContext.getEnvironment() String port = this.applicationContext.getEnvironment()
.getProperty("local.server.port", "8080"); .getProperty("local.server.port", "8080");
String baseUrl = (sslEnabled ? "https" : "http") + "://localhost:" + port; 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) { private boolean isSslEnabled(ApplicationContext context) {
...@@ -130,6 +137,18 @@ class WebTestClientContextCustomizer implements ContextCustomizer { ...@@ -130,6 +137,18 @@ class WebTestClientContextCustomizer implements ContextCustomizer {
} }
} }
private void customizeWebTestClientCodecs(WebTestClient.Builder clientBuilder,
ApplicationContext context) {
Collection<CodecCustomizer> codecCustomizers = context.getBeansOfType(CodecCustomizer.class).values();
if (!CollectionUtils.isEmpty(codecCustomizers)) {
clientBuilder.exchangeStrategies(ExchangeStrategies.builder()
.codecs(codecs -> {
codecCustomizers.forEach(codecCustomizer -> codecCustomizer.customize(codecs));
})
.build());
}
}
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment