Skip to content

Commit 5f2d2b2

Browse files
committed
Avoid required uri when using WebTestClient w/ base url
This commit makes the `uri` step of the WebTestClient optional, so that users who have specified a base URL during WebClient config do not need to provide an empty one (i.e. `url("")`). Issue: SPR-15695
1 parent 3232cb6 commit 5f2d2b2

File tree

3 files changed

+84
-67
lines changed

3 files changed

+84
-67
lines changed

spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java

Lines changed: 48 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -93,97 +93,96 @@ private Duration getTimeout() {
9393

9494

9595
@Override
96-
public UriSpec<RequestHeadersSpec<?>> get() {
97-
return toUriSpec(wc -> wc.method(HttpMethod.GET));
96+
public RequestHeadersUriSpec<?> get() {
97+
return methodInternal(HttpMethod.GET);
9898
}
9999

100100
@Override
101-
public UriSpec<RequestHeadersSpec<?>> head() {
102-
return toUriSpec(wc -> wc.method(HttpMethod.HEAD));
101+
public RequestHeadersUriSpec<?> head() {
102+
return methodInternal(HttpMethod.HEAD);
103103
}
104104

105105
@Override
106-
public UriSpec<RequestBodySpec> post() {
107-
return toUriSpec(wc -> wc.method(HttpMethod.POST));
106+
public RequestBodyUriSpec post() {
107+
return methodInternal(HttpMethod.POST);
108108
}
109109

110110
@Override
111-
public UriSpec<RequestBodySpec> put() {
112-
return toUriSpec(wc -> wc.method(HttpMethod.PUT));
111+
public RequestBodyUriSpec put() {
112+
return methodInternal(HttpMethod.PUT);
113113
}
114114

115115
@Override
116-
public UriSpec<RequestBodySpec> patch() {
117-
return toUriSpec(wc -> wc.method(HttpMethod.PATCH));
116+
public RequestBodyUriSpec patch() {
117+
return methodInternal(HttpMethod.PATCH);
118118
}
119119

120120
@Override
121-
public UriSpec<RequestHeadersSpec<?>> delete() {
122-
return toUriSpec(wc -> wc.method(HttpMethod.DELETE));
121+
public RequestHeadersUriSpec<?> delete() {
122+
return methodInternal(HttpMethod.DELETE);
123123
}
124124

125125
@Override
126-
public UriSpec<RequestHeadersSpec<?>> options() {
127-
return toUriSpec(wc -> wc.method(HttpMethod.OPTIONS));
126+
public RequestHeadersUriSpec<?> options() {
127+
return methodInternal(HttpMethod.OPTIONS);
128128
}
129129

130130
@Override
131-
public Builder mutate() {
132-
return this.builder;
131+
public RequestBodyUriSpec method(HttpMethod method) {
132+
return methodInternal(method);
133133
}
134134

135-
private <S extends RequestHeadersSpec<?>> UriSpec<S> toUriSpec(
136-
Function<WebClient, WebClient.UriSpec<WebClient.RequestBodySpec>> function) {
135+
private RequestBodyUriSpec methodInternal(HttpMethod method) {
136+
return new DefaultRequestBodyUriSpec(this.webClient.method(method));
137+
}
137138

138-
return new DefaultUriSpec<>(function.apply(this.webClient));
139+
@Override
140+
public Builder mutate() {
141+
return this.builder;
139142
}
140143

141144

142-
@SuppressWarnings("unchecked")
143-
private class DefaultUriSpec<S extends RequestHeadersSpec<?>> implements UriSpec<S> {
145+
private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
144146

145-
private final WebClient.UriSpec<WebClient.RequestBodySpec> uriSpec;
147+
private final WebClient.RequestBodyUriSpec bodySpec;
146148

149+
@Nullable
150+
private String uriTemplate;
147151

148-
DefaultUriSpec(WebClient.UriSpec<WebClient.RequestBodySpec> spec) {
149-
this.uriSpec = spec;
150-
}
152+
private final String requestId;
151153

152-
@Override
153-
public S uri(URI uri) {
154-
return (S) new DefaultRequestBodySpec(this.uriSpec.uri(uri), null);
154+
DefaultRequestBodyUriSpec(WebClient.RequestBodyUriSpec spec) {
155+
this.bodySpec = spec;
156+
this.requestId = String.valueOf(requestIndex.incrementAndGet());
157+
this.bodySpec.header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, this.requestId);
155158
}
156159

157160
@Override
158-
public S uri(String uriTemplate, Object... uriVariables) {
159-
return (S) new DefaultRequestBodySpec(this.uriSpec.uri(uriTemplate, uriVariables), uriTemplate);
161+
public RequestBodySpec uri(String uriTemplate, Object... uriVariables) {
162+
this.bodySpec.uri(uriTemplate, uriVariables);
163+
this.uriTemplate = uriTemplate;
164+
return this;
160165
}
161166

162167
@Override
163-
public S uri(String uriTemplate, Map<String, ?> uriVariables) {
164-
return (S) new DefaultRequestBodySpec(this.uriSpec.uri(uriTemplate, uriVariables), uriTemplate);
168+
public RequestBodySpec uri(String uriTemplate, Map<String, ?> uriVariables) {
169+
this.bodySpec.uri(uriTemplate, uriVariables);
170+
this.uriTemplate = uriTemplate;
171+
return this;
165172
}
166173

167174
@Override
168-
public S uri(Function<UriBuilder, URI> uriBuilder) {
169-
return (S) new DefaultRequestBodySpec(this.uriSpec.uri(uriBuilder), null);
175+
public RequestBodySpec uri(Function<UriBuilder, URI> uriFunction) {
176+
this.bodySpec.uri(uriFunction);
177+
this.uriTemplate = null;
178+
return this;
170179
}
171-
}
172-
173-
private class DefaultRequestBodySpec implements RequestBodySpec {
174180

175-
private final WebClient.RequestBodySpec bodySpec;
176-
177-
@Nullable
178-
private final String uriTemplate;
179-
180-
private final String requestId;
181-
182-
DefaultRequestBodySpec(WebClient.RequestBodySpec spec, @Nullable String uriTemplate) {
183-
this.bodySpec = spec;
184-
this.uriTemplate = uriTemplate;
185-
this.requestId = String.valueOf(requestIndex.incrementAndGet());
186-
this.bodySpec.header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, this.requestId);
181+
@Override
182+
public RequestBodySpec uri(URI uri) {
183+
this.bodySpec.uri(uri);
184+
this.uriTemplate = null;
185+
return this;
187186
}
188187

189188
@Override

spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.core.ParameterizedTypeReference;
3232
import org.springframework.format.FormatterRegistry;
3333
import org.springframework.http.HttpHeaders;
34+
import org.springframework.http.HttpMethod;
3435
import org.springframework.http.MediaType;
3536
import org.springframework.http.client.reactive.ClientHttpRequest;
3637
import org.springframework.http.codec.ServerCodecConfigurer;
@@ -89,43 +90,50 @@ public interface WebTestClient {
8990
* Prepare an HTTP GET request.
9091
* @return a spec for specifying the target URL
9192
*/
92-
UriSpec<RequestHeadersSpec<?>> get();
93+
RequestHeadersUriSpec<?> get();
9394

9495
/**
9596
* Prepare an HTTP HEAD request.
9697
* @return a spec for specifying the target URL
9798
*/
98-
UriSpec<RequestHeadersSpec<?>> head();
99+
RequestHeadersUriSpec<?> head();
99100

100101
/**
101102
* Prepare an HTTP POST request.
102103
* @return a spec for specifying the target URL
103104
*/
104-
UriSpec<RequestBodySpec> post();
105+
RequestBodyUriSpec post();
105106

106107
/**
107108
* Prepare an HTTP PUT request.
108109
* @return a spec for specifying the target URL
109110
*/
110-
UriSpec<RequestBodySpec> put();
111+
RequestBodyUriSpec put();
111112

112113
/**
113114
* Prepare an HTTP PATCH request.
114115
* @return a spec for specifying the target URL
115116
*/
116-
UriSpec<RequestBodySpec> patch();
117+
RequestBodyUriSpec patch();
117118

118119
/**
119120
* Prepare an HTTP DELETE request.
120121
* @return a spec for specifying the target URL
121122
*/
122-
UriSpec<RequestHeadersSpec<?>> delete();
123+
RequestHeadersUriSpec<?> delete();
123124

124125
/**
125126
* Prepare an HTTP OPTIONS request.
126127
* @return a spec for specifying the target URL
127128
*/
128-
UriSpec<RequestHeadersSpec<?>> options();
129+
RequestHeadersUriSpec<?> options();
130+
131+
/**
132+
* Prepare a request for the specified {@code HttpMethod}.
133+
* @return a spec for specifying the target URL
134+
*/
135+
RequestBodyUriSpec method(HttpMethod method);
136+
129137

130138

131139
/**
@@ -152,7 +160,7 @@ static ControllerSpec bindToController(Object... controllers) {
152160
* detected from an {@link ApplicationContext} such as
153161
* {@code @EnableWebFlux} Java config and annotated controller Spring beans.
154162
* @param applicationContext the context
155-
* @return the {@link WebTestClient} builder
163+
* @return the {@code WebTestClient} builder
156164
* @see org.springframework.web.reactive.config.EnableWebFlux
157165
*/
158166
static MockServerSpec<?> bindToApplicationContext(ApplicationContext applicationContext) {
@@ -162,7 +170,7 @@ static MockServerSpec<?> bindToApplicationContext(ApplicationContext application
162170
/**
163171
* Integration testing without a server targeting WebFlux functional endpoints.
164172
* @param routerFunction the RouterFunction to test
165-
* @return the {@link WebTestClient} builder
173+
* @return the {@code WebTestClient} builder
166174
*/
167175
static RouterFunctionSpec bindToRouterFunction(RouterFunction<?> routerFunction) {
168176
return new DefaultRouterFunctionSpec(routerFunction);
@@ -171,15 +179,15 @@ static RouterFunctionSpec bindToRouterFunction(RouterFunction<?> routerFunction)
171179
/**
172180
* Integration testing with a "mock" server targeting the given WebHandler.
173181
* @param webHandler the handler to test
174-
* @return the {@link WebTestClient} builder
182+
* @return the {@code WebTestClient} builder
175183
*/
176184
static MockServerSpec<?> bindToWebHandler(WebHandler webHandler) {
177185
return new DefaultMockServerSpec(webHandler);
178186
}
179187

180188
/**
181189
* Complete end-to-end integration tests with actual requests to a running server.
182-
* @return the {@link WebTestClient} builder
190+
* @return the {@code WebTestClient} builder
183191
*/
184192
static Builder bindToServer() {
185193
return new DefaultWebTestClientBuilder();
@@ -580,6 +588,14 @@ interface RequestBodySpec extends RequestHeadersSpec<RequestBodySpec> {
580588

581589
}
582590

591+
interface RequestHeadersUriSpec<S extends RequestHeadersSpec<S>>
592+
extends UriSpec<S>, RequestHeadersSpec<S> {
593+
}
594+
595+
interface RequestBodyUriSpec extends RequestBodySpec, RequestHeadersUriSpec<RequestBodySpec> {
596+
}
597+
598+
583599
/**
584600
* Spec for declaring expectations on the response.
585601
*/

spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ResponseEntityTests.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,15 @@
5151
*/
5252
public class ResponseEntityTests {
5353

54-
private final WebTestClient client = WebTestClient.bindToController(new PersonController()).build();
54+
private final WebTestClient client = WebTestClient.bindToController(new PersonController())
55+
.configureClient()
56+
.baseUrl("/persons")
57+
.build();
5558

5659

5760
@Test
5861
public void entity() throws Exception {
59-
this.client.get().uri("/persons/John")
62+
this.client.get().uri("/John")
6063
.exchange()
6164
.expectStatus().isOk()
6265
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
@@ -65,7 +68,7 @@ public void entity() throws Exception {
6568

6669
@Test
6770
public void entityWithConsumer() throws Exception {
68-
this.client.get().uri("/persons/John")
71+
this.client.get().uri("/John")
6972
.exchange()
7073
.expectStatus().isOk()
7174
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
@@ -79,7 +82,7 @@ public void entityList() throws Exception {
7982
List<Person> expected = Arrays.asList(
8083
new Person("Jane"), new Person("Jason"), new Person("John"));
8184

82-
this.client.get().uri("/persons")
85+
this.client.get()
8386
.exchange()
8487
.expectStatus().isOk()
8588
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
@@ -94,7 +97,7 @@ public void entityMap() throws Exception {
9497
map.put("Jason", new Person("Jason"));
9598
map.put("John", new Person("John"));
9699

97-
this.client.get().uri("/persons?map=true")
100+
this.client.get().uri("?map=true")
98101
.exchange()
99102
.expectStatus().isOk()
100103
.expectBody(new ParameterizedTypeReference<Map<String, Person>>() {}).isEqualTo(map);
@@ -104,7 +107,6 @@ public void entityMap() throws Exception {
104107
public void entityStream() throws Exception {
105108

106109
FluxExchangeResult<Person> result = this.client.get()
107-
.uri("/persons")
108110
.accept(TEXT_EVENT_STREAM)
109111
.exchange()
110112
.expectStatus().isOk()
@@ -121,7 +123,7 @@ public void entityStream() throws Exception {
121123

122124
@Test
123125
public void postEntity() throws Exception {
124-
this.client.post().uri("/persons")
126+
this.client.post()
125127
.syncBody(new Person("John"))
126128
.exchange()
127129
.expectStatus().isCreated()

0 commit comments

Comments
 (0)