Skip to content

Commit f2e77e4

Browse files
committed
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
1 parent 6477720 commit f2e77e4

File tree

4 files changed

+143
-8
lines changed

4 files changed

+143
-8
lines changed

spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfiguration.java

+28-2
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@
1616

1717
package org.springframework.boot.test.autoconfigure.web.reactive;
1818

19+
import java.util.Collection;
20+
21+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
1922
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2023
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
24+
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
25+
import org.springframework.boot.web.codec.CodecCustomizer;
2126
import org.springframework.context.ApplicationContext;
2227
import org.springframework.context.annotation.Bean;
2328
import org.springframework.context.annotation.Configuration;
2429
import org.springframework.test.web.reactive.server.WebTestClient;
30+
import org.springframework.util.CollectionUtils;
31+
import org.springframework.web.reactive.function.client.ExchangeStrategies;
2532
import org.springframework.web.reactive.function.client.WebClient;
2633

2734
/**
@@ -31,13 +38,32 @@
3138
* @since 2.0.0
3239
*/
3340
@Configuration
34-
@ConditionalOnClass({ WebClient.class, WebTestClient.class })
41+
@ConditionalOnClass({WebClient.class, WebTestClient.class})
42+
@AutoConfigureAfter(CodecsAutoConfiguration.class)
3543
public class WebTestClientAutoConfiguration {
3644

3745
@Bean
3846
@ConditionalOnMissingBean
3947
public WebTestClient webTestClient(ApplicationContext applicationContext) {
40-
return WebTestClient.bindToApplicationContext(applicationContext).build();
48+
49+
WebTestClient.Builder clientBuilder = WebTestClient
50+
.bindToApplicationContext(applicationContext).configureClient();
51+
customizeWebTestClientCodecs(clientBuilder, applicationContext);
52+
return clientBuilder.build();
53+
}
54+
55+
private void customizeWebTestClientCodecs(WebTestClient.Builder clientBuilder,
56+
ApplicationContext applicationContext) {
57+
58+
Collection<CodecCustomizer> codecCustomizers = applicationContext
59+
.getBeansOfType(CodecCustomizer.class).values();
60+
if (!CollectionUtils.isEmpty(codecCustomizers)) {
61+
clientBuilder.exchangeStrategies(ExchangeStrategies.builder()
62+
.codecs(codecs -> {
63+
codecCustomizers.forEach(codecCustomizer -> codecCustomizer.customize(codecs));
64+
})
65+
.build());
66+
}
4167
}
4268

4369
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure.web.reactive;
18+
19+
import org.junit.After;
20+
import org.junit.Test;
21+
22+
import org.springframework.boot.web.codec.CodecCustomizer;
23+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.context.annotation.Import;
27+
import org.springframework.http.codec.CodecConfigurer;
28+
import org.springframework.test.web.reactive.server.WebTestClient;
29+
import org.springframework.web.server.WebHandler;
30+
31+
import static org.assertj.core.api.Assertions.assertThat;
32+
import static org.mockito.ArgumentMatchers.any;
33+
import static org.mockito.Mockito.mock;
34+
import static org.mockito.Mockito.verify;
35+
36+
/**
37+
* Tests for {@link WebTestClientAutoConfiguration}
38+
*
39+
* @author Brian Clozel
40+
*/
41+
public class WebTestClientAutoConfigurationTests {
42+
43+
private AnnotationConfigApplicationContext context;
44+
45+
@After
46+
public void close() {
47+
if (this.context != null) {
48+
this.context.close();
49+
}
50+
}
51+
52+
@Test
53+
public void shouldCustomizeClientCodecs() throws Exception {
54+
load(CodecConfiguration.class);
55+
WebTestClient webTestClient = this.context.getBean(WebTestClient.class);
56+
CodecCustomizer codecCustomizer = this.context.getBean(CodecCustomizer.class);
57+
assertThat(webTestClient).isNotNull();
58+
verify(codecCustomizer).customize(any(CodecConfigurer.class));
59+
}
60+
61+
62+
private void load(Class<?>... config) {
63+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
64+
ctx.register(config);
65+
ctx.register(WebTestClientAutoConfiguration.class);
66+
ctx.refresh();
67+
this.context = ctx;
68+
}
69+
70+
@Configuration
71+
static class BaseConfiguration {
72+
73+
@Bean
74+
public WebHandler webHandler() {
75+
return mock(WebHandler.class);
76+
}
77+
}
78+
79+
@Configuration
80+
@Import(BaseConfiguration.class)
81+
static class CodecConfiguration {
82+
83+
@Bean
84+
public CodecCustomizer myCodecCustomizer() {
85+
return mock(CodecCustomizer.class);
86+
}
87+
88+
}
89+
90+
}

spring-boot-test/pom.xml

+5-5
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@
105105
<artifactId>spring-web</artifactId>
106106
<optional>true</optional>
107107
</dependency>
108+
<dependency>
109+
<groupId>org.springframework</groupId>
110+
<artifactId>spring-webflux</artifactId>
111+
<optional>true</optional>
112+
</dependency>
108113
<dependency>
109114
<groupId>net.sourceforge.htmlunit</groupId>
110115
<artifactId>htmlunit</artifactId>
@@ -162,11 +167,6 @@
162167
<artifactId>spring-webmvc</artifactId>
163168
<scope>test</scope>
164169
</dependency>
165-
<dependency>
166-
<groupId>org.springframework</groupId>
167-
<artifactId>spring-webflux</artifactId>
168-
<scope>test</scope>
169-
</dependency>
170170
</dependencies>
171171
<build>
172172
<plugins>

spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizer.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616

1717
package org.springframework.boot.test.web.reactive;
1818

19+
import java.util.Collection;
20+
1921
import org.springframework.beans.BeansException;
2022
import org.springframework.beans.factory.FactoryBean;
2123
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2224
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
2325
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
2426
import org.springframework.beans.factory.support.RootBeanDefinition;
2527
import org.springframework.boot.test.context.SpringBootTest;
28+
import org.springframework.boot.web.codec.CodecCustomizer;
2629
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory;
2730
import org.springframework.context.ApplicationContext;
2831
import org.springframework.context.ApplicationContextAware;
@@ -31,6 +34,8 @@
3134
import org.springframework.test.context.ContextCustomizer;
3235
import org.springframework.test.context.MergedContextConfiguration;
3336
import org.springframework.test.web.reactive.server.WebTestClient;
37+
import org.springframework.util.CollectionUtils;
38+
import org.springframework.web.reactive.function.client.ExchangeStrategies;
3439

3540
/**
3641
* {@link ContextCustomizer} for {@link WebTestClient}.
@@ -115,7 +120,9 @@ private WebTestClient createWebTestClient() {
115120
String port = this.applicationContext.getEnvironment()
116121
.getProperty("local.server.port", "8080");
117122
String baseUrl = (sslEnabled ? "https" : "http") + "://localhost:" + port;
118-
return WebTestClient.bindToServer().baseUrl(baseUrl).build();
123+
WebTestClient.Builder builder = WebTestClient.bindToServer();
124+
customizeWebTestClientCodecs(builder, this.applicationContext);
125+
return builder.baseUrl(baseUrl).build();
119126
}
120127

121128
private boolean isSslEnabled(ApplicationContext context) {
@@ -130,6 +137,18 @@ private boolean isSslEnabled(ApplicationContext context) {
130137
}
131138
}
132139

140+
private void customizeWebTestClientCodecs(WebTestClient.Builder clientBuilder,
141+
ApplicationContext context) {
142+
Collection<CodecCustomizer> codecCustomizers = context.getBeansOfType(CodecCustomizer.class).values();
143+
if (!CollectionUtils.isEmpty(codecCustomizers)) {
144+
clientBuilder.exchangeStrategies(ExchangeStrategies.builder()
145+
.codecs(codecs -> {
146+
codecCustomizers.forEach(codecCustomizer -> codecCustomizer.customize(codecs));
147+
})
148+
.build());
149+
}
150+
}
151+
133152
}
134153

135154
}

0 commit comments

Comments
 (0)