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<T> (e.g. for use with a StepVerifier).
This commit is contained in:
@@ -253,56 +253,76 @@ class DefaultWebTestClient implements WebTestClient {
|
||||
return toResponseSpec(this.headerSpec.exchange(publisher, elementClass));
|
||||
}
|
||||
|
||||
private DefaultResponseSpec toResponseSpec(Mono<ClientResponse> responseMono) {
|
||||
ClientResponse response = responseMono.block(getTimeout());
|
||||
ClientHttpRequest request = webTestClientConnector.claimRequest(this.requestId);
|
||||
ExchangeResult<Flux<DataBuffer>> result = ExchangeResult.create(request, response);
|
||||
return new DefaultResponseSpec(result, response);
|
||||
private DefaultResponseSpec toResponseSpec(Mono<ClientResponse> mono) {
|
||||
ClientResponse response = mono.block(getTimeout());
|
||||
ClientHttpRequest httpRequest = webTestClientConnector.claimRequest(this.requestId);
|
||||
return new DefaultResponseSpec(httpRequest, response);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class ResponseSpecSupport {
|
||||
|
||||
private final ExchangeResult<Flux<DataBuffer>> exchangeResult;
|
||||
/**
|
||||
* ExchangeResult that contains the live {@link ClientResponse}.
|
||||
*/
|
||||
private class UndecodedExchangeResult extends ExchangeResult {
|
||||
|
||||
private final ClientResponse response;
|
||||
|
||||
|
||||
public ResponseSpecSupport(ExchangeResult<Flux<DataBuffer>> result, ClientResponse response) {
|
||||
this.exchangeResult = result;
|
||||
public UndecodedExchangeResult(ClientHttpRequest httpRequest, ClientResponse response) {
|
||||
super(httpRequest, response);
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
|
||||
protected ExchangeResult<Flux<DataBuffer>> 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<List<?>> 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 <T> ExchangeResult<T> createResultWithDecodedBody(T body) {
|
||||
return ExchangeResult.withDecodedBody(this.exchangeResult, body);
|
||||
public <T> FluxExchangeResult<T> decodeBody(ResolvableType elementType) {
|
||||
Flux<T> body = this.response.body(toFlux(elementType));
|
||||
return new FluxExchangeResult<>(this, body, elementType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public EntityExchangeResult<Map<?, ?>> consumeMap(ResolvableType keyType, ResolvableType valueType) {
|
||||
ResolvableType mapType = ResolvableType.forClassWithGenerics(Map.class, keyType, valueType);
|
||||
return (EntityExchangeResult<Map<?, ?>>) consumeSingle(mapType);
|
||||
}
|
||||
|
||||
public EntityExchangeResult<Void> 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<Flux<DataBuffer>> 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<ExchangeResult<Flux<DataBuffer>>> consumer) {
|
||||
consumer.accept(getExchangeResult());
|
||||
public ResponseSpec consumeWith(Consumer<ExchangeResult> consumer) {
|
||||
consumer.accept(this.exchangeResult);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExchangeResult<Flux<DataBuffer>> 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<List<?>> completed = this.exchangeResult.consumeList(this.elementType, count);
|
||||
return new DefaultListBodySpec(completed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ExchangeResult<Flux<T>> returnResult() {
|
||||
Flux<T> flux = getResponse().body(toFlux(this.elementType));
|
||||
return createResultWithDecodedBody(flux);
|
||||
public <T> FluxExchangeResult<T> 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 <T> ExchangeResult<T> isEqualTo(Object expected) {
|
||||
assertEquals("Response body", expected, this.body);
|
||||
public <T> EntityExchangeResult<T> isEqualTo(T expected) {
|
||||
assertEquals("Response body", expected, this.exchangeResult.getResponseBody());
|
||||
return returnResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> ExchangeResult<T> returnResult() {
|
||||
return createResultWithDecodedBody((T) this.body);
|
||||
public <T> EntityExchangeResult<T> 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<List<?>> 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<List<?>> result) {
|
||||
this.exchangeResult = result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> ExchangeResult<List<T>> isEqualTo(List<T> expected) {
|
||||
assertEquals("Response body", expected, this.body);
|
||||
public <T> EntityExchangeResult<List<T>> isEqualTo(List<T> expected) {
|
||||
assertEquals("Response body", expected, this.exchangeResult.getResponseBody());
|
||||
return returnResult();
|
||||
}
|
||||
|
||||
@@ -419,7 +435,7 @@ class DefaultWebTestClient implements WebTestClient {
|
||||
public ListBodySpec contains(Object... elements) {
|
||||
List<Object> 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<Object> 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 <T> ExchangeResult<List<T>> returnResult() {
|
||||
return createResultWithDecodedBody((List<T>) this.body);
|
||||
public <T> EntityExchangeResult<List<T>> returnResult() {
|
||||
return new EntityExchangeResult<>(this.exchangeResult, (List<T>) 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<Void> isEmpty() {
|
||||
DataBuffer buffer = getResponse().body(toDataBuffers()).blockFirst(getTimeout());
|
||||
assertTrue("Expected empty body", buffer == null);
|
||||
return createResultWithDecodedBody(null);
|
||||
public EntityExchangeResult<Void> 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<Map<?, ?>> 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<Map<?, ?>> 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<Map<?, ?>> result) {
|
||||
this.exchangeResult = result;
|
||||
}
|
||||
|
||||
|
||||
private Map<?, ?> getBody() {
|
||||
return this.exchangeResult.getResponseBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> ExchangeResult<Map<K, V>> isEqualTo(Map<K, V> expected) {
|
||||
public <K, V> EntityExchangeResult<Map<K, V>> isEqualTo(Map<K, V> 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 <K, V> ExchangeResult<Map<K, V>> returnResult() {
|
||||
return createResultWithDecodedBody((Map<K, V>) this.body);
|
||||
public <K, V> EntityExchangeResult<Map<K, V>> returnResult() {
|
||||
return new EntityExchangeResult<>(this.exchangeResult, (Map<K, V>) getBody());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <T>}.
|
||||
*
|
||||
* @param <T> the response body type
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class EntityExchangeResult<T> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <T> the type of the response body
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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<T> {
|
||||
public class ExchangeResult {
|
||||
|
||||
private final HttpMethod method;
|
||||
|
||||
@@ -50,84 +50,59 @@ public class ExchangeResult<T> {
|
||||
|
||||
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<Flux<DataBuffer>> 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 <T> ExchangeResult<T> 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<T> {
|
||||
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<T> {
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
protected String formatResponseBody() {
|
||||
return "Not read yet";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<T>}.
|
||||
*
|
||||
* @param <T> the type of elements in the response body
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class FluxExchangeResult<T> extends ExchangeResult {
|
||||
|
||||
private final Flux<T> body;
|
||||
|
||||
private final ResolvableType elementType;
|
||||
|
||||
|
||||
FluxExchangeResult(ExchangeResult exchange, Flux<T> body, ResolvableType elementType) {
|
||||
super(exchange);
|
||||
this.body = body;
|
||||
this.elementType = elementType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the {@code Flux} of elements decoded from the response body.
|
||||
*/
|
||||
public Flux<T> getResponseBody() {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatResponseBody() {
|
||||
return "Flux<" + this.elementType.toString() + ">";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<ExchangeResult<Flux<DataBuffer>>> consumer);
|
||||
ResponseSpec consumeWith(Consumer<ExchangeResult> 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<Flux<DataBuffer>> 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<T>} and available for example for use
|
||||
* with a {@code StepVerifier} from Project Reactor.
|
||||
*/
|
||||
<T> ExchangeResult<Flux<T>> returnResult();
|
||||
<T> FluxExchangeResult<T> returnResult();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -529,13 +527,13 @@ public interface WebTestClient {
|
||||
/**
|
||||
* Assert the extracted body is equal to the given value.
|
||||
*/
|
||||
<T> ExchangeResult<T> isEqualTo(Object expected);
|
||||
<T> EntityExchangeResult<T> 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.
|
||||
*/
|
||||
<T> ExchangeResult<T> returnResult();
|
||||
<T> EntityExchangeResult<T> returnResult();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -546,7 +544,7 @@ public interface WebTestClient {
|
||||
/**
|
||||
* Assert the extracted body is equal to the given list.
|
||||
*/
|
||||
<T> ExchangeResult<List<T>> isEqualTo(List<T> expected);
|
||||
<T> EntityExchangeResult<List<T>> isEqualTo(List<T> 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.
|
||||
*/
|
||||
<T> ExchangeResult<List<T>> returnResult();
|
||||
<T> EntityExchangeResult<List<T>> 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<Void> isEmpty();
|
||||
EntityExchangeResult<Void> 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.
|
||||
*/
|
||||
<K, V> ExchangeResult<Map<K, V>> isEqualTo(Map<K, V> expected);
|
||||
<K, V> EntityExchangeResult<Map<K, V>> isEqualTo(Map<K, V> 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.
|
||||
*/
|
||||
<K, V> ExchangeResult<Map<K, V>> returnResult();
|
||||
<K, V> EntityExchangeResult<Map<K, V>> returnResult();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user