Skip to content

Commit a8a7782

Browse files
committed
Verify that WebClient hypermedia autoconfig works for WebTestClient.
* Write test cases that verify WebTestClient will pick up the same autoconfiguration beans created for WebClient. * Verify it handles one mediatype, multiple mediatypes, and custom mediatypes. Related: #16020. Resolves #20372.
1 parent 9bc160d commit a8a7782

File tree

5 files changed

+150
-8
lines changed

5 files changed

+150
-8
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hateoas/HypermediaAutoConfiguration.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,9 @@
4242
import org.springframework.hateoas.config.HypermediaMappingInformation;
4343
import org.springframework.http.codec.json.Jackson2JsonDecoder;
4444
import org.springframework.http.codec.json.Jackson2JsonEncoder;
45-
import org.springframework.plugin.core.Plugin;
4645
import org.springframework.util.Assert;
4746
import org.springframework.util.MimeType;
48-
import org.springframework.web.bind.annotation.RequestMapping;
4947
import org.springframework.web.reactive.function.client.WebClient;
50-
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
5148

5249
/**
5350
* {@link EnableAutoConfiguration Auto-configuration} for Spring HATEOAS's
@@ -60,7 +57,7 @@
6057
* @since 1.1.0
6158
*/
6259
@Configuration(proxyBeanMethods = false)
63-
@ConditionalOnClass({ EntityModel.class, RequestMapping.class, RequestMappingHandlerAdapter.class, Plugin.class })
60+
@ConditionalOnClass({ EntityModel.class })
6461
@ConditionalOnWebApplication
6562
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
6663
HttpMessageConvertersAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class })
@@ -80,7 +77,7 @@ protected static class HypermediaConfiguration {
8077
* Define beans needed to autoconfigure {@link WebClient}.
8178
*/
8279
@Configuration(proxyBeanMethods = false)
83-
@ConditionalOnClass({ WebClient.class, HypermediaMappingInformation.class, ObjectMapper.class,
80+
@ConditionalOnClass({ HypermediaMappingInformation.class, ObjectMapper.class,
8481
Jackson2JsonDecoder.class })
8582
protected static class WebClientHypermediaConfiguration {
8683

spring-boot-project/spring-boot-test-autoconfigure/build.gradle

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,8 @@ dependencies {
7272
testImplementation("org.junit.platform:junit-platform-engine")
7373
testImplementation("org.junit.jupiter:junit-jupiter")
7474
testImplementation("org.mockito:mockito-core")
75-
testImplementation("org.skyscreamer:jsonassert")
7675
testImplementation("org.springframework.hateoas:spring-hateoas")
77-
testImplementation("org.springframework.plugin:spring-plugin-core")
76+
testImplementation("org.skyscreamer:jsonassert")
7877
testImplementation("org.testcontainers:junit-jupiter")
7978
testImplementation("org.testcontainers:neo4j")
8079
testImplementation("org.testcontainers:testcontainers")

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2525
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2626
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
27+
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
2728
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
2829
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
2930
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -48,7 +49,8 @@
4849
*/
4950
@Configuration(proxyBeanMethods = false)
5051
@ConditionalOnClass({ WebClient.class, WebTestClient.class })
51-
@AutoConfigureAfter({ CodecsAutoConfiguration.class, WebFluxAutoConfiguration.class })
52+
@AutoConfigureAfter({ CodecsAutoConfiguration.class, WebFluxAutoConfiguration.class,
53+
HypermediaAutoConfiguration.class })
5254
@Import(WebTestClientSecurityConfiguration.class)
5355
@EnableConfigurationProperties
5456
public class WebTestClientAutoConfiguration {

spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration
9696

9797
# AutoConfigureWebClient auto-configuration imports
9898
org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient=\
99+
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
99100
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
100101
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
101102
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
@@ -140,6 +141,7 @@ org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfigu
140141
org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient=\
141142
org.springframework.boot.test.autoconfigure.web.client.WebClientRestTemplateAutoConfiguration,\
142143
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
144+
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
143145
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
144146
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
145147
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright 2012-2020 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+
* https://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.hateoas;
18+
19+
import java.util.Collections;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
24+
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
25+
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
26+
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
27+
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
28+
import org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration;
29+
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.context.annotation.Configuration;
32+
import org.springframework.hateoas.MediaTypes;
33+
import org.springframework.hateoas.config.EnableHypermediaSupport;
34+
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
35+
import org.springframework.hateoas.config.HypermediaMappingInformation;
36+
import org.springframework.http.MediaType;
37+
import org.springframework.http.codec.HttpMessageReader;
38+
import org.springframework.http.codec.HttpMessageWriter;
39+
import org.springframework.test.util.ReflectionTestUtils;
40+
import org.springframework.test.web.reactive.server.WebTestClient;
41+
import org.springframework.web.reactive.config.EnableWebFlux;
42+
import org.springframework.web.reactive.function.client.ExchangeStrategies;
43+
import org.springframework.web.reactive.function.client.WebClient;
44+
45+
import static org.assertj.core.api.Assertions.assertThat;
46+
47+
/**
48+
* Tests for hypermedia-based {@link WebTestClient}.
49+
*
50+
* @author Brian Clozel
51+
* @author Stephane Nicoll
52+
* @author Greg Turnquist
53+
*/
54+
class HypermediaWebTestClientAutoConfigurationTests {
55+
56+
static MediaType FRODO_JSON = MediaType.parseMediaType("application/frodo+json");
57+
58+
private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
59+
.withUserConfiguration(BaseConfiguration.class);
60+
61+
@Test
62+
void codecsCustomizerShouldRegisterHypermediaTypesWithWebTestClient() {
63+
this.contextRunner.withUserConfiguration(HalConfig.class).run((context) -> {
64+
WebTestClient webTestClient = context.getBean(WebTestClient.class);
65+
assertWebTestClientHasHypermedia(webTestClient, MediaTypes.HAL_JSON);
66+
});
67+
}
68+
69+
@Test
70+
void codecsCustomizerShouldRegisterAlternativeHypermediaTypesWithWebTestClient() {
71+
this.contextRunner.withUserConfiguration(HalFormsConfig.class).run((context) -> {
72+
WebTestClient webTestClient = context.getBean(WebTestClient.class);
73+
assertWebTestClientHasHypermedia(webTestClient, MediaTypes.HAL_FORMS_JSON);
74+
});
75+
}
76+
77+
@Test
78+
void codecsCustomizerShouldRegisterAllHypermediaTypesWithWebTestClient() {
79+
this.contextRunner.withUserConfiguration(AllHypermediaConfig.class).run((context) -> {
80+
WebTestClient webTestClient = context.getBean(WebTestClient.class);
81+
assertWebTestClientHasHypermedia(webTestClient, MediaTypes.HAL_JSON, MediaTypes.HAL_FORMS_JSON,
82+
MediaTypes.COLLECTION_JSON, MediaTypes.UBER_JSON);
83+
});
84+
}
85+
86+
@Test
87+
void codecsCustomizerShouldRegisterCustomHypermediaTypesWithWebTestClient() {
88+
this.contextRunner.withUserConfiguration(CustomHypermediaConfig.class).run((context) -> {
89+
WebTestClient webTestClient = context.getBean(WebTestClient.class);
90+
assertWebTestClientHasHypermedia(webTestClient, MediaTypes.HAL_JSON, FRODO_JSON);
91+
});
92+
}
93+
94+
private static void assertWebTestClientHasHypermedia(WebTestClient webTestClient, MediaType... mediaTypes) {
95+
WebClient webClient = (WebClient) ReflectionTestUtils.getField(webTestClient, "webClient");
96+
ExchangeStrategies strategies = (ExchangeStrategies) ReflectionTestUtils
97+
.getField(ReflectionTestUtils.getField(webClient, "exchangeFunction"), "strategies");
98+
99+
assertThat(strategies.messageReaders()).flatExtracting(HttpMessageReader::getReadableMediaTypes)
100+
.contains(mediaTypes);
101+
assertThat(strategies.messageWriters()).flatExtracting(HttpMessageWriter::getWritableMediaTypes)
102+
.contains(mediaTypes);
103+
}
104+
105+
@ImportAutoConfiguration({ CodecsAutoConfiguration.class, WebFluxAutoConfiguration.class,
106+
JacksonAutoConfiguration.class, HypermediaAutoConfiguration.class, WebTestClientAutoConfiguration.class })
107+
@EnableWebFlux
108+
static class BaseConfiguration {
109+
110+
}
111+
112+
@Configuration(proxyBeanMethods = false)
113+
@EnableHypermediaSupport(type = HypermediaType.HAL)
114+
static class HalConfig {
115+
116+
}
117+
118+
@Configuration(proxyBeanMethods = false)
119+
@EnableHypermediaSupport(type = HypermediaType.HAL_FORMS)
120+
static class HalFormsConfig {
121+
122+
}
123+
124+
@Configuration(proxyBeanMethods = false)
125+
@EnableHypermediaSupport(type = { HypermediaType.HAL, HypermediaType.HAL_FORMS, HypermediaType.COLLECTION_JSON,
126+
HypermediaType.UBER })
127+
static class AllHypermediaConfig {
128+
129+
}
130+
131+
@Configuration(proxyBeanMethods = false)
132+
@EnableHypermediaSupport(type = HypermediaType.HAL)
133+
static class CustomHypermediaConfig {
134+
135+
@Bean
136+
HypermediaMappingInformation frodoMediaType() {
137+
return () -> Collections.singletonList(FRODO_JSON);
138+
}
139+
140+
}
141+
142+
}

0 commit comments

Comments
 (0)