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
This commit is contained in:
Arjen Poutsma 2017-07-07 13:38:25 +02:00
parent 3232cb6260
commit 5f2d2b21d9
3 changed files with 91 additions and 74 deletions

View File

@ -93,38 +93,47 @@ class DefaultWebTestClient implements WebTestClient {
@Override
public UriSpec<RequestHeadersSpec<?>> get() {
return toUriSpec(wc -> wc.method(HttpMethod.GET));
public RequestHeadersUriSpec<?> get() {
return methodInternal(HttpMethod.GET);
}
@Override
public UriSpec<RequestHeadersSpec<?>> head() {
return toUriSpec(wc -> wc.method(HttpMethod.HEAD));
public RequestHeadersUriSpec<?> head() {
return methodInternal(HttpMethod.HEAD);
}
@Override
public UriSpec<RequestBodySpec> post() {
return toUriSpec(wc -> wc.method(HttpMethod.POST));
public RequestBodyUriSpec post() {
return methodInternal(HttpMethod.POST);
}
@Override
public UriSpec<RequestBodySpec> put() {
return toUriSpec(wc -> wc.method(HttpMethod.PUT));
public RequestBodyUriSpec put() {
return methodInternal(HttpMethod.PUT);
}
@Override
public UriSpec<RequestBodySpec> patch() {
return toUriSpec(wc -> wc.method(HttpMethod.PATCH));
public RequestBodyUriSpec patch() {
return methodInternal(HttpMethod.PATCH);
}
@Override
public UriSpec<RequestHeadersSpec<?>> delete() {
return toUriSpec(wc -> wc.method(HttpMethod.DELETE));
public RequestHeadersUriSpec<?> delete() {
return methodInternal(HttpMethod.DELETE);
}
@Override
public UriSpec<RequestHeadersSpec<?>> options() {
return toUriSpec(wc -> wc.method(HttpMethod.OPTIONS));
public RequestHeadersUriSpec<?> options() {
return methodInternal(HttpMethod.OPTIONS);
}
@Override
public RequestBodyUriSpec method(HttpMethod method) {
return methodInternal(method);
}
private RequestBodyUriSpec methodInternal(HttpMethod method) {
return new DefaultRequestBodyUriSpec(this.webClient.method(method));
}
@Override
@ -132,60 +141,50 @@ class DefaultWebTestClient implements WebTestClient {
return this.builder;
}
private <S extends RequestHeadersSpec<?>> UriSpec<S> toUriSpec(
Function<WebClient, WebClient.UriSpec<WebClient.RequestBodySpec>> function) {
return new DefaultUriSpec<>(function.apply(this.webClient));
}
private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
@SuppressWarnings("unchecked")
private class DefaultUriSpec<S extends RequestHeadersSpec<?>> implements UriSpec<S> {
private final WebClient.UriSpec<WebClient.RequestBodySpec> uriSpec;
DefaultUriSpec(WebClient.UriSpec<WebClient.RequestBodySpec> spec) {
this.uriSpec = spec;
}
@Override
public S uri(URI uri) {
return (S) new DefaultRequestBodySpec(this.uriSpec.uri(uri), null);
}
@Override
public S uri(String uriTemplate, Object... uriVariables) {
return (S) new DefaultRequestBodySpec(this.uriSpec.uri(uriTemplate, uriVariables), uriTemplate);
}
@Override
public S uri(String uriTemplate, Map<String, ?> uriVariables) {
return (S) new DefaultRequestBodySpec(this.uriSpec.uri(uriTemplate, uriVariables), uriTemplate);
}
@Override
public S uri(Function<UriBuilder, URI> uriBuilder) {
return (S) new DefaultRequestBodySpec(this.uriSpec.uri(uriBuilder), null);
}
}
private class DefaultRequestBodySpec implements RequestBodySpec {
private final WebClient.RequestBodySpec bodySpec;
private final WebClient.RequestBodyUriSpec bodySpec;
@Nullable
private final String uriTemplate;
private String uriTemplate;
private final String requestId;
DefaultRequestBodySpec(WebClient.RequestBodySpec spec, @Nullable String uriTemplate) {
DefaultRequestBodyUriSpec(WebClient.RequestBodyUriSpec spec) {
this.bodySpec = spec;
this.uriTemplate = uriTemplate;
this.requestId = String.valueOf(requestIndex.incrementAndGet());
this.bodySpec.header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, this.requestId);
}
@Override
public RequestBodySpec uri(String uriTemplate, Object... uriVariables) {
this.bodySpec.uri(uriTemplate, uriVariables);
this.uriTemplate = uriTemplate;
return this;
}
@Override
public RequestBodySpec uri(String uriTemplate, Map<String, ?> uriVariables) {
this.bodySpec.uri(uriTemplate, uriVariables);
this.uriTemplate = uriTemplate;
return this;
}
@Override
public RequestBodySpec uri(Function<UriBuilder, URI> uriFunction) {
this.bodySpec.uri(uriFunction);
this.uriTemplate = null;
return this;
}
@Override
public RequestBodySpec uri(URI uri) {
this.bodySpec.uri(uri);
this.uriTemplate = null;
return this;
}
@Override
public RequestBodySpec header(String headerName, String... headerValues) {
this.bodySpec.header(headerName, headerValues);

View File

@ -31,6 +31,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.http.codec.ServerCodecConfigurer;
@ -89,43 +90,50 @@ public interface WebTestClient {
* Prepare an HTTP GET request.
* @return a spec for specifying the target URL
*/
UriSpec<RequestHeadersSpec<?>> get();
RequestHeadersUriSpec<?> get();
/**
* Prepare an HTTP HEAD request.
* @return a spec for specifying the target URL
*/
UriSpec<RequestHeadersSpec<?>> head();
RequestHeadersUriSpec<?> head();
/**
* Prepare an HTTP POST request.
* @return a spec for specifying the target URL
*/
UriSpec<RequestBodySpec> post();
RequestBodyUriSpec post();
/**
* Prepare an HTTP PUT request.
* @return a spec for specifying the target URL
*/
UriSpec<RequestBodySpec> put();
RequestBodyUriSpec put();
/**
* Prepare an HTTP PATCH request.
* @return a spec for specifying the target URL
*/
UriSpec<RequestBodySpec> patch();
RequestBodyUriSpec patch();
/**
* Prepare an HTTP DELETE request.
* @return a spec for specifying the target URL
*/
UriSpec<RequestHeadersSpec<?>> delete();
RequestHeadersUriSpec<?> delete();
/**
* Prepare an HTTP OPTIONS request.
* @return a spec for specifying the target URL
*/
UriSpec<RequestHeadersSpec<?>> options();
RequestHeadersUriSpec<?> options();
/**
* Prepare a request for the specified {@code HttpMethod}.
* @return a spec for specifying the target URL
*/
RequestBodyUriSpec method(HttpMethod method);
/**
@ -152,7 +160,7 @@ public interface WebTestClient {
* detected from an {@link ApplicationContext} such as
* {@code @EnableWebFlux} Java config and annotated controller Spring beans.
* @param applicationContext the context
* @return the {@link WebTestClient} builder
* @return the {@code WebTestClient} builder
* @see org.springframework.web.reactive.config.EnableWebFlux
*/
static MockServerSpec<?> bindToApplicationContext(ApplicationContext applicationContext) {
@ -162,7 +170,7 @@ public interface WebTestClient {
/**
* Integration testing without a server targeting WebFlux functional endpoints.
* @param routerFunction the RouterFunction to test
* @return the {@link WebTestClient} builder
* @return the {@code WebTestClient} builder
*/
static RouterFunctionSpec bindToRouterFunction(RouterFunction<?> routerFunction) {
return new DefaultRouterFunctionSpec(routerFunction);
@ -171,7 +179,7 @@ public interface WebTestClient {
/**
* Integration testing with a "mock" server targeting the given WebHandler.
* @param webHandler the handler to test
* @return the {@link WebTestClient} builder
* @return the {@code WebTestClient} builder
*/
static MockServerSpec<?> bindToWebHandler(WebHandler webHandler) {
return new DefaultMockServerSpec(webHandler);
@ -179,7 +187,7 @@ public interface WebTestClient {
/**
* Complete end-to-end integration tests with actual requests to a running server.
* @return the {@link WebTestClient} builder
* @return the {@code WebTestClient} builder
*/
static Builder bindToServer() {
return new DefaultWebTestClientBuilder();
@ -580,6 +588,14 @@ public interface WebTestClient {
}
interface RequestHeadersUriSpec<S extends RequestHeadersSpec<S>>
extends UriSpec<S>, RequestHeadersSpec<S> {
}
interface RequestBodyUriSpec extends RequestBodySpec, RequestHeadersUriSpec<RequestBodySpec> {
}
/**
* Spec for declaring expectations on the response.
*/

View File

@ -51,12 +51,15 @@ import static org.springframework.http.MediaType.TEXT_EVENT_STREAM;
*/
public class ResponseEntityTests {
private final WebTestClient client = WebTestClient.bindToController(new PersonController()).build();
private final WebTestClient client = WebTestClient.bindToController(new PersonController())
.configureClient()
.baseUrl("/persons")
.build();
@Test
public void entity() throws Exception {
this.client.get().uri("/persons/John")
this.client.get().uri("/John")
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
@ -65,7 +68,7 @@ public class ResponseEntityTests {
@Test
public void entityWithConsumer() throws Exception {
this.client.get().uri("/persons/John")
this.client.get().uri("/John")
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
@ -79,7 +82,7 @@ public class ResponseEntityTests {
List<Person> expected = Arrays.asList(
new Person("Jane"), new Person("Jason"), new Person("John"));
this.client.get().uri("/persons")
this.client.get()
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
@ -94,7 +97,7 @@ public class ResponseEntityTests {
map.put("Jason", new Person("Jason"));
map.put("John", new Person("John"));
this.client.get().uri("/persons?map=true")
this.client.get().uri("?map=true")
.exchange()
.expectStatus().isOk()
.expectBody(new ParameterizedTypeReference<Map<String, Person>>() {}).isEqualTo(map);
@ -104,7 +107,6 @@ public class ResponseEntityTests {
public void entityStream() throws Exception {
FluxExchangeResult<Person> result = this.client.get()
.uri("/persons")
.accept(TEXT_EVENT_STREAM)
.exchange()
.expectStatus().isOk()
@ -121,7 +123,7 @@ public class ResponseEntityTests {
@Test
public void postEntity() throws Exception {
this.client.post().uri("/persons")
this.client.post()
.syncBody(new Person("John"))
.exchange()
.expectStatus().isCreated()