From d1a64e1122335834731613764efc3cc63e96c920 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Sun, 19 Feb 2017 20:45:22 -0500 Subject: [PATCH] Refactor ExchangeResult Clearly separate how the result of an exchange is represented before the response body has been read (e.g. assertions on status and headers only) vs later after the body is extracted to a representation (assertions on the extracted body) or is decoded to Flux (e.g. for use with a StepVerifier). --- .../reactive/server/DefaultWebTestClient.java | 184 ++++++++++-------- .../reactive/server/EntityExchangeResult.java | 50 +++++ .../web/reactive/server/ExchangeResult.java | 91 ++++----- .../reactive/server/FluxExchangeResult.java | 56 ++++++ .../web/reactive/server/HeaderAssertions.java | 8 +- .../web/reactive/server/StatusAssertions.java | 8 +- .../web/reactive/server/WebTestClient.java | 63 +++--- .../server/samples/ResponseEntityTests.java | 4 +- 8 files changed, 284 insertions(+), 180 deletions(-) create mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/EntityExchangeResult.java create mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/FluxExchangeResult.java diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java index 25c8a82b61..20341cbc86 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java @@ -253,56 +253,76 @@ class DefaultWebTestClient implements WebTestClient { return toResponseSpec(this.headerSpec.exchange(publisher, elementClass)); } - private DefaultResponseSpec toResponseSpec(Mono responseMono) { - ClientResponse response = responseMono.block(getTimeout()); - ClientHttpRequest request = webTestClientConnector.claimRequest(this.requestId); - ExchangeResult> result = ExchangeResult.create(request, response); - return new DefaultResponseSpec(result, response); + private DefaultResponseSpec toResponseSpec(Mono mono) { + ClientResponse response = mono.block(getTimeout()); + ClientHttpRequest httpRequest = webTestClientConnector.claimRequest(this.requestId); + return new DefaultResponseSpec(httpRequest, response); } } - private abstract class ResponseSpecSupport { - - private final ExchangeResult> exchangeResult; + /** + * ExchangeResult that contains the live {@link ClientResponse}. + */ + private class UndecodedExchangeResult extends ExchangeResult { private final ClientResponse response; - public ResponseSpecSupport(ExchangeResult> result, ClientResponse response) { - this.exchangeResult = result; + public UndecodedExchangeResult(ClientHttpRequest httpRequest, ClientResponse response) { + super(httpRequest, response); this.response = response; } - protected ExchangeResult> getExchangeResult() { - return this.exchangeResult; + public EntityExchangeResult consumeSingle(ResolvableType elementType) { + Object body = this.response.body(toMono(elementType)).block(getTimeout()); + return new EntityExchangeResult<>(this, body); } - protected ClientResponse getResponse() { - return this.response; + public EntityExchangeResult> consumeList(ResolvableType elementType, int count) { + Flux flux = this.response.body(toFlux(elementType)); + if (count >= 0) { + flux = flux.take(count); + } + List body = flux.collectList().block(getTimeout()); + return new EntityExchangeResult<>(this, body); } - protected ExchangeResult createResultWithDecodedBody(T body) { - return ExchangeResult.withDecodedBody(this.exchangeResult, body); + public FluxExchangeResult decodeBody(ResolvableType elementType) { + Flux body = this.response.body(toFlux(elementType)); + return new FluxExchangeResult<>(this, body, elementType); } + @SuppressWarnings("unchecked") + public EntityExchangeResult> consumeMap(ResolvableType keyType, ResolvableType valueType) { + ResolvableType mapType = ResolvableType.forClassWithGenerics(Map.class, keyType, valueType); + return (EntityExchangeResult>) consumeSingle(mapType); + } + + public EntityExchangeResult consumeEmpty() { + DataBuffer buffer = this.response.body(toDataBuffers()).blockFirst(getTimeout()); + assertTrue("Expected empty body", buffer == null); + return new EntityExchangeResult<>(this, null); + } } - private class DefaultResponseSpec extends ResponseSpecSupport implements ResponseSpec { + private class DefaultResponseSpec implements ResponseSpec { + + private final UndecodedExchangeResult exchangeResult; - public DefaultResponseSpec(ExchangeResult> exchangeResult, ClientResponse response) { - super(exchangeResult, response); + public DefaultResponseSpec(ClientHttpRequest httpRequest, ClientResponse response) { + this.exchangeResult = new UndecodedExchangeResult(httpRequest, response); } @Override public StatusAssertions expectStatus() { - return new StatusAssertions(getExchangeResult(), this); + return new StatusAssertions(this.exchangeResult, this); } @Override public HeaderAssertions expectHeader() { - return new HeaderAssertions(getExchangeResult(), this); + return new HeaderAssertions(this.exchangeResult, this); } @Override @@ -312,40 +332,43 @@ class DefaultWebTestClient implements WebTestClient { @Override public TypeBodySpec expectBody(ResolvableType elementType) { - return new DefaultTypeBodySpec(this, elementType); + return new DefaultTypeBodySpec(this.exchangeResult, elementType); } @Override public BodySpec expectBody() { - return new DefaultBodySpec(this); + return new DefaultBodySpec(this.exchangeResult); } @Override - public ResponseSpec consumeWith(Consumer>> consumer) { - consumer.accept(getExchangeResult()); + public ResponseSpec consumeWith(Consumer consumer) { + consumer.accept(this.exchangeResult); return this; } @Override - public ExchangeResult> returnResult() { - return getExchangeResult(); + public ExchangeResult returnResult() { + return this.exchangeResult; } } - private class DefaultTypeBodySpec extends ResponseSpecSupport implements TypeBodySpec { + private class DefaultTypeBodySpec implements TypeBodySpec { + + private final UndecodedExchangeResult exchangeResult; private final ResolvableType elementType; - public DefaultTypeBodySpec(DefaultResponseSpec spec, ResolvableType elementType) { - super(spec.getExchangeResult(), spec.getResponse()); + public DefaultTypeBodySpec(UndecodedExchangeResult result, ResolvableType elementType) { + this.exchangeResult = result; this.elementType = elementType; } @Override public SingleValueBodySpec value() { - return new DefaultSingleValueBodySpec(this, this.elementType); + EntityExchangeResult completed = this.exchangeResult.consumeSingle(this.elementType); + return new DefaultSingleValueBodySpec(completed); } @Override @@ -354,59 +377,52 @@ class DefaultWebTestClient implements WebTestClient { } @Override - public ListBodySpec list(int elementCount) { - return new DefaultListBodySpec(this, this.elementType, elementCount); + public ListBodySpec list(int count) { + EntityExchangeResult> completed = this.exchangeResult.consumeList(this.elementType, count); + return new DefaultListBodySpec(completed); } @Override - public ExchangeResult> returnResult() { - Flux flux = getResponse().body(toFlux(this.elementType)); - return createResultWithDecodedBody(flux); + public FluxExchangeResult returnResult() { + return this.exchangeResult.decodeBody(this.elementType); } } - private class DefaultSingleValueBodySpec extends ResponseSpecSupport implements SingleValueBodySpec { + private class DefaultSingleValueBodySpec implements SingleValueBodySpec { - private final Object body; + private final EntityExchangeResult exchangeResult; - public DefaultSingleValueBodySpec(DefaultTypeBodySpec spec, ResolvableType elementType) { - super(spec.getExchangeResult(), spec.getResponse()); - this.body = getResponse().body(toMono(elementType)).block(getTimeout()); + public DefaultSingleValueBodySpec(EntityExchangeResult result) { + this.exchangeResult = result; } @Override - public ExchangeResult isEqualTo(Object expected) { - assertEquals("Response body", expected, this.body); + public EntityExchangeResult isEqualTo(T expected) { + assertEquals("Response body", expected, this.exchangeResult.getResponseBody()); return returnResult(); } @Override - @SuppressWarnings("unchecked") - public ExchangeResult returnResult() { - return createResultWithDecodedBody((T) this.body); + public EntityExchangeResult returnResult() { + return new EntityExchangeResult<>(this.exchangeResult, (T) this.exchangeResult.getResponseBody()); } } - private class DefaultListBodySpec extends ResponseSpecSupport implements ListBodySpec { + private class DefaultListBodySpec implements ListBodySpec { - private final List body; + private final EntityExchangeResult> exchangeResult; - public DefaultListBodySpec(DefaultTypeBodySpec spec, ResolvableType elementType, int elementCount) { - super(spec.getExchangeResult(), spec.getResponse()); - Flux flux = getResponse().body(toFlux(elementType)); - if (elementCount >= 0) { - flux = flux.take(elementCount); - } - this.body = flux.collectList().block(getTimeout()); + public DefaultListBodySpec(EntityExchangeResult> result) { + this.exchangeResult = result; } @Override - public ExchangeResult> isEqualTo(List expected) { - assertEquals("Response body", expected, this.body); + public EntityExchangeResult> isEqualTo(List expected) { + assertEquals("Response body", expected, this.exchangeResult.getResponseBody()); return returnResult(); } @@ -419,7 +435,7 @@ class DefaultWebTestClient implements WebTestClient { public ListBodySpec contains(Object... elements) { List elementList = Arrays.asList(elements); String message = "Response body does not contain " + elementList; - assertTrue(message, this.body.containsAll(elementList)); + assertTrue(message, this.exchangeResult.getResponseBody().containsAll(elementList)); return this; } @@ -427,30 +443,30 @@ class DefaultWebTestClient implements WebTestClient { public ListBodySpec doesNotContain(Object... elements) { List elementList = Arrays.asList(elements); String message = "Response body should have contained " + elementList; - assertTrue(message, !this.body.containsAll(Arrays.asList(elements))); + assertTrue(message, !this.exchangeResult.getResponseBody().containsAll(Arrays.asList(elements))); return this; } @Override @SuppressWarnings("unchecked") - public ExchangeResult> returnResult() { - return createResultWithDecodedBody((List) this.body); + public EntityExchangeResult> returnResult() { + return new EntityExchangeResult<>(this.exchangeResult, (List) this.exchangeResult.getResponseBody()); } } - private class DefaultBodySpec extends ResponseSpecSupport implements BodySpec { + private class DefaultBodySpec implements BodySpec { + + private final UndecodedExchangeResult exchangeResult; - public DefaultBodySpec(DefaultResponseSpec spec) { - super(spec.getExchangeResult(), spec.getResponse()); + public DefaultBodySpec(UndecodedExchangeResult result) { + this.exchangeResult = result; } @Override - public ExchangeResult isEmpty() { - DataBuffer buffer = getResponse().body(toDataBuffers()).blockFirst(getTimeout()); - assertTrue("Expected empty body", buffer == null); - return createResultWithDecodedBody(null); + public EntityExchangeResult isEmpty() { + return this.exchangeResult.consumeEmpty(); } @Override @@ -460,57 +476,61 @@ class DefaultWebTestClient implements WebTestClient { @Override public MapBodySpec map(ResolvableType keyType, ResolvableType valueType) { - return new DefaultMapBodySpec(this, keyType, valueType); + EntityExchangeResult> completed = this.exchangeResult.consumeMap(keyType, valueType); + return new DefaultMapBodySpec(completed); } } - private class DefaultMapBodySpec extends ResponseSpecSupport implements MapBodySpec { + private class DefaultMapBodySpec implements MapBodySpec { - private final Map body; + private final EntityExchangeResult> exchangeResult; - public DefaultMapBodySpec(DefaultBodySpec spec, ResolvableType keyType, ResolvableType valueType) { - super(spec.getExchangeResult(), spec.getResponse()); - ResolvableType mapType = ResolvableType.forClassWithGenerics(Map.class, keyType, valueType); - this.body = (Map) spec.getResponse().body(toMono(mapType)).block(getTimeout()); + public DefaultMapBodySpec(EntityExchangeResult> result) { + this.exchangeResult = result; } + private Map getBody() { + return this.exchangeResult.getResponseBody(); + } + @Override - public ExchangeResult> isEqualTo(Map expected) { + public EntityExchangeResult> isEqualTo(Map expected) { + assertEquals("Response body map", expected, getBody()); return returnResult(); } @Override public MapBodySpec hasSize(int size) { - assertEquals("Response body map size", size, this.body.size()); + assertEquals("Response body map size", size, getBody().size()); return this; } @Override public MapBodySpec contains(Object key, Object value) { - assertEquals("Response body map value for key " + key, value, this.body.get(key)); + assertEquals("Response body map value for key " + key, value, getBody().get(key)); return this; } @Override public MapBodySpec containsKeys(Object... keys) { - List missing = Arrays.stream(keys).filter(k -> !this.body.containsKey(k)).collect(toList()); + List missing = Arrays.stream(keys).filter(k -> !getBody().containsKey(k)).collect(toList()); assertTrue("Response body map does not contain keys " + missing, missing.isEmpty()); return this; } @Override public MapBodySpec containsValues(Object... values) { - List missing = Arrays.stream(values).filter(v -> !this.body.containsValue(v)).collect(toList()); + List missing = Arrays.stream(values).filter(v -> !getBody().containsValue(v)).collect(toList()); assertTrue("Response body map does not contain values " + missing, missing.isEmpty()); return this; } @Override @SuppressWarnings("unchecked") - public ExchangeResult> returnResult() { - return createResultWithDecodedBody((Map) this.body); + public EntityExchangeResult> returnResult() { + return new EntityExchangeResult<>(this.exchangeResult, (Map) getBody()); } } diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/EntityExchangeResult.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/EntityExchangeResult.java new file mode 100644 index 0000000000..32a619f8b2 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/EntityExchangeResult.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-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.test.web.reactive.server; + +/** + * {@code ExchangeResult} variant with the response body fully extracted to a + * representation of type {@code }. + * + * @param the response body type + * + * @author Rossen Stoyanchev + * @since 5.0 + */ +public class EntityExchangeResult extends ExchangeResult { + + private final T body; + + + EntityExchangeResult(ExchangeResult exchange, T body) { + super(exchange); + this.body = body; + } + + + /** + * Return the body extracted from the response. + */ + public T getResponseBody() { + return this.body; + } + + @Override + protected String formatResponseBody() { + return this.body.toString(); + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeResult.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeResult.java index 24318aee1b..e5f5308e57 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeResult.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeResult.java @@ -18,27 +18,27 @@ package org.springframework.test.web.reactive.server; import java.net.URI; import java.util.stream.Collectors; -import reactor.core.publisher.Flux; - -import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.client.reactive.ClientHttpRequest; import org.springframework.web.reactive.function.client.ClientResponse; -import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers; - /** - * Container for request and response details from an exchange performed - * through {@link WebTestClient}. + * Container for the result of an exchange through the {@link WebTestClient}. * - * @param the type of the response body + *

This type only exposes the status and response headers that are available + * when the {@link ClientResponse} is first received and before the response + * body has been consumed. + * + *

The sub-classes {@link EntityExchangeResult} and {@link FluxExchangeResult} + * expose further information about the response body and are returned only + * after the test client has been used to decode and consume the response. * * @author Rossen Stoyanchev * @since 5.0 */ -public class ExchangeResult { +public class ExchangeResult { private final HttpMethod method; @@ -50,84 +50,59 @@ public class ExchangeResult { private final HttpHeaders responseHeaders; - private final T responseBody; + ExchangeResult(ClientHttpRequest request, ClientResponse response) { + this.method = request.getMethod(); + this.url = request.getURI(); + this.requestHeaders = request.getHeaders(); + this.status = response.statusCode(); + this.responseHeaders = response.headers().asHttpHeaders(); + } - /** - * Package private constructor. - */ - ExchangeResult(HttpMethod method, URI url, HttpHeaders requestHeaders, HttpStatus status, - HttpHeaders responseHeaders, T responseBody) { - - this.method = method; - this.url = url; - this.requestHeaders = requestHeaders; - this.status = status; - this.responseHeaders = responseHeaders; - this.responseBody = responseBody; + ExchangeResult(ExchangeResult exchange) { + this.method = exchange.getMethod(); + this.url = exchange.getUrl(); + this.requestHeaders = exchange.getRequestHeaders(); + this.status = exchange.getStatus(); + this.responseHeaders = exchange.getResponseHeaders(); } /** - * Return the request method of the exchange. + * Return the method of the request. */ public HttpMethod getMethod() { return this.method; } /** - * Return the URL of the exchange. + * Return the request headers that were sent to the server. */ public URI getUrl() { return this.url; } /** - * Return the request headers of the exchange. + * Return the request headers sent to the server. */ public HttpHeaders getRequestHeaders() { return this.requestHeaders; } /** - * Return the response status. + * Return the status of the executed request. */ public HttpStatus getStatus() { return this.status; } /** - * Return the response headers. + * Return the response headers received from the server. */ public HttpHeaders getResponseHeaders() { return this.responseHeaders; } - /** - * Return the decoded response body. - */ - public T getResponseBody() { - return this.responseBody; - } - - - /** - * Create from ClientHttpRequest and ClientResponse (body not yet consumed). - */ - static ExchangeResult> create(ClientHttpRequest request, ClientResponse response) { - return new ExchangeResult<>(request.getMethod(), request.getURI(), request.getHeaders(), - response.statusCode(), response.headers().asHttpHeaders(), - response.body(toDataBuffers())); - } - - /** - * Re-create with decoded body (possibly still not consumed). - */ - static ExchangeResult withDecodedBody(ExchangeResult result, T body) { - return new ExchangeResult<>(result.getMethod(), result.getUrl(), result.getRequestHeaders(), - result.getStatus(), result.getResponseHeaders(), body); - } - @Override public String toString() { @@ -135,10 +110,10 @@ public class ExchangeResult { return "\n\n" + formatValue("Request", this.method + " " + getUrl()) + formatValue("Status", status + " " + status.getReasonPhrase()) + - formatHeading("Response Headers") + - formatHeaders(this.responseHeaders) + - formatHeading("Request Headers") + - formatHeaders(this.requestHeaders); + formatHeading("Response Headers") + formatHeaders(this.responseHeaders) + + formatHeading("Request Headers") + formatHeaders(this.requestHeaders) + + "\n" + + formatValue("Response Body", formatResponseBody()); } private String formatHeading(String heading) { @@ -155,4 +130,8 @@ public class ExchangeResult { .collect(Collectors.joining()); } + protected String formatResponseBody() { + return "Not read yet"; + } + } diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/FluxExchangeResult.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/FluxExchangeResult.java new file mode 100644 index 0000000000..c90a4d2c3b --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/FluxExchangeResult.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-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.test.web.reactive.server; + +import reactor.core.publisher.Flux; + +import org.springframework.core.ResolvableType; + +/** + * {@code ExchangeResult} variant with the response body as a {@code Flux}. + * + * @param the type of elements in the response body + * + * @author Rossen Stoyanchev + * @since 5.0 + */ +public class FluxExchangeResult extends ExchangeResult { + + private final Flux body; + + private final ResolvableType elementType; + + + FluxExchangeResult(ExchangeResult exchange, Flux body, ResolvableType elementType) { + super(exchange); + this.body = body; + this.elementType = elementType; + } + + + /** + * Return the {@code Flux} of elements decoded from the response body. + */ + public Flux getResponseBody() { + return this.body; + } + + @Override + protected String formatResponseBody() { + return "Flux<" + this.elementType.toString() + ">"; + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java index e7269511ed..4a79210ead 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java @@ -37,14 +37,14 @@ import static org.springframework.test.util.AssertionErrors.assertTrue; */ public class HeaderAssertions { - private final ExchangeResult exchangeResult; + private final ExchangeResult exchangeResult; private final WebTestClient.ResponseSpec responseSpec; - HeaderAssertions(ExchangeResult exchangeResult, WebTestClient.ResponseSpec responseSpec) { - this.exchangeResult = exchangeResult; - this.responseSpec = responseSpec; + HeaderAssertions(ExchangeResult result, WebTestClient.ResponseSpec spec) { + this.exchangeResult = result; + this.responseSpec = spec; } diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/StatusAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/StatusAssertions.java index 5e5a24cbab..e53229db00 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/StatusAssertions.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/StatusAssertions.java @@ -29,14 +29,14 @@ import static org.springframework.test.util.AssertionErrors.assertEquals; @SuppressWarnings("unused") public class StatusAssertions { - private final ExchangeResult exchangeResult; + private final ExchangeResult exchangeResult; private final WebTestClient.ResponseSpec responseSpec; - StatusAssertions(ExchangeResult exchangeResult, WebTestClient.ResponseSpec responseSpec) { - this.exchangeResult = exchangeResult; - this.responseSpec = responseSpec; + StatusAssertions(ExchangeResult result, WebTestClient.ResponseSpec spec) { + this.exchangeResult = result; + this.responseSpec = spec; } diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java index ed7d97b00f..60cd6599b7 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java @@ -25,11 +25,9 @@ import java.util.function.Consumer; import java.util.function.Function; import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; import org.springframework.context.ApplicationContext; import org.springframework.core.ResolvableType; -import org.springframework.core.io.buffer.DataBuffer; import org.springframework.format.FormatterRegistry; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -464,7 +462,7 @@ public interface WebTestClient { /** * Assertions on the body of the response extracted to one or more - * elements of the given type. + * representations of the given type. */ TypeBodySpec expectBody(Class elementType); @@ -474,24 +472,23 @@ public interface WebTestClient { TypeBodySpec expectBody(ResolvableType elementType); /** - * Access to additional assertions on the response body -- - * isEmpty, map, and others. + * Other assertions on the response body -- isEmpty, map, etc. */ BodySpec expectBody(); /** - * Consume the {@link ExchangeResult} and continue with expectations. - * The {@code ExchangeResult} is parameterized with data buffers since - * the body is not yet consumed nor decoded at this level. + * Consume request and response details of the exchange. Only status + * response headers are available at this stage before one of the + * {@code expectBody} methods is used. */ - ResponseSpec consumeWith(Consumer>> consumer); + ResponseSpec consumeWith(Consumer consumer); /** - * Return a container for the result of the exchange. The returned - * {@code ExchangeResult} is parameterized with data buffers since - * the body is not yet consumed nor decoded at this level. + * Return the request and response details of the exchange. Only status + * and response headers are available at this stage before one of the + * {@code expectBody} methods is used. */ - ExchangeResult> returnResult(); + ExchangeResult returnResult(); } /** @@ -500,25 +497,26 @@ public interface WebTestClient { interface TypeBodySpec { /** - * Extract a single value from the response. + * Extract a single representations from the response. */ SingleValueBodySpec value(); /** - * Extract a list of values from the response. + * Extract a list of representations from the response. */ ListBodySpec list(); /** - * Extract a list of values consuming the first N elements. + * Extract a list of representations consuming the first N elements. */ ListBodySpec list(int elementCount); /** - * Return a container for the result of the exchange parameterized with - * the {@code Flux} of decoded objects (not yet consumed). + * Return request and response details from the exchange including the + * response body as a {@code Flux} and available for example for use + * with a {@code StepVerifier} from Project Reactor. */ - ExchangeResult> returnResult(); + FluxExchangeResult returnResult(); } /** @@ -529,13 +527,13 @@ public interface WebTestClient { /** * Assert the extracted body is equal to the given value. */ - ExchangeResult isEqualTo(Object expected); + EntityExchangeResult isEqualTo(T expected); /** - * Return a container for the result of the exchange parameterized with - * the extracted response entity. + * Return request and response details from the exchange including the + * extracted response body. */ - ExchangeResult returnResult(); + EntityExchangeResult returnResult(); } /** @@ -546,7 +544,7 @@ public interface WebTestClient { /** * Assert the extracted body is equal to the given list. */ - ExchangeResult> isEqualTo(List expected); + EntityExchangeResult> isEqualTo(List expected); /** * Assert the extracted list of values is of the given size. @@ -567,10 +565,10 @@ public interface WebTestClient { ListBodySpec doesNotContain(Object... elements); /** - * Return a container for the result of the exchange parameterized with - * the extracted list of response entities. + * Return request and response details from the exchange including the + * extracted response body. */ - ExchangeResult> returnResult(); + EntityExchangeResult> returnResult(); } /** @@ -580,9 +578,9 @@ public interface WebTestClient { /** * Consume the body and verify it is empty. - * @return container for the result of the exchange + * @return request and response details from the exchange */ - ExchangeResult isEmpty(); + EntityExchangeResult isEmpty(); /** * Extract the response body as a Map with the given key and value type. @@ -604,7 +602,7 @@ public interface WebTestClient { /** * Assert the extracted map is equal to the given list of elements. */ - ExchangeResult> isEqualTo(Map expected); + EntityExchangeResult> isEqualTo(Map expected); /** * Assert the extracted map has the given size. @@ -632,9 +630,10 @@ public interface WebTestClient { MapBodySpec containsValues(Object... values); /** - * Return a container for the result of the exchange. + * Return request and response details from the exchange including the + * extracted response body. */ - ExchangeResult> returnResult(); + EntityExchangeResult> returnResult(); } } diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ResponseEntityTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ResponseEntityTests.java index 9ef604a387..009e610394 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ResponseEntityTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ResponseEntityTests.java @@ -31,7 +31,7 @@ import reactor.test.StepVerifier; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.test.web.reactive.server.ExchangeResult; +import org.springframework.test.web.reactive.server.FluxExchangeResult; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -101,7 +101,7 @@ public class ResponseEntityTests { @Test public void entityStream() throws Exception { - ExchangeResult> result = this.client.get() + FluxExchangeResult result = this.client.get() .uri("/persons") .accept(TEXT_EVENT_STREAM) .exchange()