From 51f2042e9759c7f0575fcf133cd53dac4c44bfba Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 15 Feb 2017 18:10:57 -0500 Subject: [PATCH] Refactor WebTestClient assertions Rather than returning ExchangeActions that contains ExchangeInfo and applies a chain of assertions, the exchange operation in WebTestClient now has an extra step to decode the response. The outcome of that is ExchangeResult, parameterized with the decoded body type, and containing the request and response details, also providing access to built-inassertions via an assertThat() instance method. This approach lends itself better to decoding and asserting response body types with generecis. It is also more friendly to using any assertion library such as AssertJ since you get the result first and then deal with assertions. --- .../reactive/server/DefaultWebTestClient.java | 99 ++- .../web/reactive/server/ExchangeActions.java | 98 --- ...{ExchangeInfo.java => ExchangeResult.java} | 77 ++- ...sAssertions.java => HeaderAssertions.java} | 66 +- .../web/reactive/server/ListAssertions.java | 72 --- ...hangeConsumer.java => LoggingActions.java} | 47 +- .../web/reactive/server/MapAssertions.java | 94 --- .../server/MultiValueMapEntryAssertions.java | 107 ---- .../web/reactive/server/ObjectAssertions.java | 129 ---- .../reactive/server/ResponseAssertions.java | 66 ++ .../server/ResponseBodyAssertions.java | 75 --- .../server/ResponseEntityAssertions.java | 83 --- .../server/ResponseHeaderAssertions.java | 47 -- .../server/ResponseStatusAssertions.java | 606 ------------------ .../web/reactive/server/StatusAssertions.java | 200 ++++++ .../web/reactive/server/StringAssertions.java | 72 --- .../web/reactive/server/WebTestClient.java | 128 +++- .../reactive/server/samples/ErrorTests.java | 9 +- .../reactive/server/samples/HeaderTests.java | 19 +- .../server/samples/ResponseEntityTests.java | 65 +- .../samples/bind/ApplicationContextTests.java | 6 +- .../server/samples/bind/ControllerTests.java | 6 +- .../server/samples/bind/HttpServerTests.java | 6 +- .../samples/bind/RouterFunctionTests.java | 6 +- 24 files changed, 635 insertions(+), 1548 deletions(-) delete mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeActions.java rename spring-test/src/main/java/org/springframework/test/web/reactive/server/{ExchangeInfo.java => ExchangeResult.java} (52%) rename spring-test/src/main/java/org/springframework/test/web/reactive/server/{ResponseHeadersAssertions.java => HeaderAssertions.java} (50%) delete mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/ListAssertions.java rename spring-test/src/main/java/org/springframework/test/web/reactive/server/{LoggingExchangeConsumer.java => LoggingActions.java} (60%) delete mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/MapAssertions.java delete mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/MultiValueMapEntryAssertions.java delete mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/ObjectAssertions.java create mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseAssertions.java delete mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseBodyAssertions.java delete mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseEntityAssertions.java delete mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseHeaderAssertions.java delete mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseStatusAssertions.java create mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/StatusAssertions.java delete mode 100644 spring-test/src/main/java/org/springframework/test/web/reactive/server/StringAssertions.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 f1d7b6f191..5535cadc2f 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 @@ -19,6 +19,7 @@ import java.net.URI; import java.nio.charset.Charset; import java.time.Duration; import java.time.ZonedDateTime; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -27,12 +28,16 @@ import java.util.function.Consumer; import java.util.function.Function; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import org.springframework.core.ResolvableType; +import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ClientHttpConnector; import org.springframework.http.client.reactive.ClientHttpRequest; +import org.springframework.test.util.AssertionErrors; import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; @@ -41,6 +46,10 @@ import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.util.UriBuilder; +import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers; +import static org.springframework.web.reactive.function.BodyExtractors.toFlux; +import static org.springframework.web.reactive.function.BodyExtractors.toMono; + /** * Default implementation of {@link WebTestClient}. * @@ -231,35 +240,91 @@ class DefaultWebTestClient implements WebTestClient { return this; } - @Override - public ExchangeActions exchange() { - return getExchangeActions(this.headerSpec.exchange()); + public ResponseSpec exchange() { + return new DefaultResponseSpec(this.requestId, this.headerSpec.exchange()); } @Override - public ExchangeActions exchange(BodyInserter inserter) { - return getExchangeActions(this.headerSpec.exchange(inserter)); + public ResponseSpec exchange(BodyInserter inserter) { + return new DefaultResponseSpec(this.requestId, this.headerSpec.exchange(inserter)); } @Override - public > ExchangeActions exchange(S publisher, Class elementClass) { - return getExchangeActions(this.headerSpec.exchange(publisher, elementClass)); + public > ResponseSpec exchange(S publisher, Class elementClass) { + return new DefaultResponseSpec(this.requestId, this.headerSpec.exchange(publisher, elementClass)); + } + } + + private class DefaultResponseSpec implements ResponseSpec { + + private final String requestId; + + private final Mono responseMono; + + + public DefaultResponseSpec(String requestId, Mono responseMono) { + this.requestId = requestId; + this.responseMono = responseMono; } - private ExchangeActions getExchangeActions(Mono responseMono) { - ClientResponse response = responseMono.block(getTimeout()); - ExchangeInfo info = getExchangeInfo(response); - return new ExchangeActions(info); + + @Override + public ExchangeResult decodeEntity(Class entityClass) { + return decodeEntity(ResolvableType.forClass(entityClass)); } - private ExchangeInfo getExchangeInfo(ClientResponse clientResponse) { - WiretapConnector.Info wiretapInfo = connectorListener.retrieveRequest(this.requestId); + @Override + public ExchangeResult> decodeAndCollect(Class elementClass) { + return decodeAndCollect(ResolvableType.forClass(elementClass)); + } + + @Override + public ExchangeResult> decodeFlux(Class elementClass) { + return decodeFlux(ResolvableType.forClass(elementClass)); + } + + @Override + public ExchangeResult decodeEntity(ResolvableType elementType) { + return this.responseMono.then(response -> { + Mono entityMono = response.body(toMono(elementType)); + return entityMono.map(entity -> createTestExchange(entity, response)); + }).block(getTimeout()); + } + + @Override + public ExchangeResult> decodeAndCollect(ResolvableType elementType) { + return this.responseMono.then(response -> { + Flux entityFlux = response.body(toFlux(elementType)); + return entityFlux.collectList().map(list -> createTestExchange(list, response)); + }).block(getTimeout()); + } + + @Override + public ExchangeResult> decodeFlux(ResolvableType elementType) { + return this.responseMono.map(response -> { + Flux entityFlux = response.body(toFlux(elementType)); + return createTestExchange(entityFlux, response); + }).block(getTimeout()); + } + + @Override + public ExchangeResult expectNoBody() { + return this.responseMono.map(response -> { + DataBuffer buffer = response.body(toDataBuffers()).blockFirst(getTimeout()); + AssertionErrors.assertTrue("Expected empty body", buffer == null); + ExchangeResult exchange = createTestExchange(null, response); + return exchange; + }).block(getTimeout()); + } + + private ExchangeResult createTestExchange(T body, ClientResponse response) { + WiretapConnector.Info wiretapInfo = connectorListener.retrieveRequest(requestId); ClientHttpRequest request = wiretapInfo.getRequest(); - return new ExchangeInfo(request.getMethod(), request.getURI(), request.getHeaders(), - clientResponse, getTimeout()); + return new ExchangeResult( + request.getMethod(), request.getURI(), request.getHeaders(), + response.statusCode(), response.headers().asHttpHeaders(), body); } - } @@ -287,7 +352,7 @@ class DefaultWebTestClient implements WebTestClient { public WiretapConnector.Info retrieveRequest(String requestId) { WiretapConnector.Info info = this.exchanges.remove(requestId); - Assert.notNull(info, "No match for \"request-id\"=" + requestId); + Assert.notNull(info, "No match for request-id=" + requestId); return info; } } diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeActions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeActions.java deleted file mode 100644 index 84e79b0ce2..0000000000 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeActions.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 java.util.Collections; -import java.util.List; - -import org.springframework.core.ResolvableType; -import org.springframework.http.HttpHeaders; - -/** - * Entry point for applying assertions and actions on a performed exchange. - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -public final class ExchangeActions { - - private final ExchangeInfo exchangeInfo; - - - public ExchangeActions(ExchangeInfo info) { - this.exchangeInfo = info; - } - - - /** - * Assert the status of the response. - * @return further options for asserting the status of the response - */ - public ResponseStatusAssertions assertStatus() { - return new ResponseStatusAssertions(this); - } - - /** - * Assert specific, commonly used response headers. - * @return further options for asserting headers - */ - public ResponseHeadersAssertions assertHeaders() { - HttpHeaders headers = this.exchangeInfo.getResponse().headers().asHttpHeaders(); - return new ResponseHeadersAssertions(this, headers); - } - - /** - * Assert value(s) of the specified header name. - * @param headerName the header name - * @return options for asserting the header value(s) - */ - public ResponseHeaderAssertions assertHeader(String headerName) { - HttpHeaders headers = this.exchangeInfo.getResponse().headers().asHttpHeaders(); - List values = headers.getOrDefault(headerName, Collections.emptyList()); - return new ResponseHeaderAssertions(this, headerName, values); - } - - /** - * Assertions on the body of the response decoded as one or more response - * entities of the given type. - * @return options for asserting response entities - */ - public ResponseEntityAssertions assertEntity(Class entityType) { - return new ResponseEntityAssertions(this, ResolvableType.forClass(entityType)); - } - - /** - * Assertions on the body of the response. - */ - public ResponseBodyAssertions assertBody() { - return new ResponseBodyAssertions(this); - } - - /** - * Log debug information about the exchange. - */ - public LoggingExchangeConsumer log() { - return new LoggingExchangeConsumer(this); - } - - /** - * Return {@link ExchangeInfo} for direct access to request and response. - */ - public ExchangeInfo andReturn() { - return this.exchangeInfo; - } - -} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeInfo.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeResult.java similarity index 52% rename from spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeInfo.java rename to spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeResult.java index a33d211acf..72097b2d32 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeInfo.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeResult.java @@ -16,21 +16,28 @@ package org.springframework.test.web.reactive.server; import java.net.URI; -import java.time.Duration; import java.util.stream.Collectors; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -import org.springframework.web.reactive.function.client.ClientResponse; /** - * Contains information about a performed exchange. + * Container for request and response details including the decoded response + * body from an exchange performed through {@link WebTestClient}. + * + *

Use {@link #assertThat()} to access built-in assertions on the response, + * or apply other assertions directly to the data contained in this class. + * The built-in assertions provide an option for logging diagnostic information + * about the exchange. The same can also be obtained using the + * {@link #toString()} method of this class. + * + * @param the type of the decoded response body * * @author Rossen Stoyanchev * @since 5.0 */ -public class ExchangeInfo { +public class ExchangeResult { private final HttpMethod method; @@ -38,33 +45,44 @@ public class ExchangeInfo { private final HttpHeaders requestHeaders; - private final ClientResponse response; + private final HttpStatus status; - private final Duration responseTimeout; + private final HttpHeaders responseHeaders; + + private final T responseBody; - public ExchangeInfo(HttpMethod httpMethod, URI uri, HttpHeaders requestHeaders, - ClientResponse response, Duration responseTimeout) { + public ExchangeResult(HttpMethod method, URI url, HttpHeaders requestHeaders, + HttpStatus status, HttpHeaders responseHeaders, T responseBody) { - this.method = httpMethod; - this.url = uri; + this.method = method; + this.url = url; this.requestHeaders = requestHeaders; - this.response = response; - this.responseTimeout = responseTimeout; + this.status = status; + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; } /** - * Return the HTTP method of the exchange. + * Provides access to built-in assertions on the response. */ - public HttpMethod getHttpMethod() { + public ResponseAssertions assertThat() { + return new ResponseAssertions(this); + } + + + /** + * Return the request method of the exchange. + */ + public HttpMethod getRequestMethod() { return this.method; } /** - * Return the URI of the exchange. + * Return the URL of the exchange. */ - public URI getUrl() { + public URI getRequestUrl() { return this.url; } @@ -76,30 +94,37 @@ public class ExchangeInfo { } /** - * Return the {@link ClientResponse} for the exchange. + * Return the response status. */ - public ClientResponse getResponse() { - return this.response; + public HttpStatus getResponseStatus() { + return this.status; } /** - * Return the configured timeout for blocking on response data. + * Return the response headers. */ - public Duration getResponseTimeout() { - return this.responseTimeout; + public HttpHeaders getResponseHeaders() { + return this.responseHeaders; + } + + /** + * Return the decoded response body. + */ + public T getResponseBody() { + return this.responseBody; } @Override public String toString() { - HttpStatus status = getResponse().statusCode(); + HttpStatus status = this.status; return "\n\n" + - formatValue("Request", getHttpMethod() + " " + getUrl()) + + formatValue("Request", this.method + " " + getRequestUrl()) + formatValue("Status", status + " " + status.getReasonPhrase()) + formatHeading("Response Headers") + - formatHeaders(getResponse().headers().asHttpHeaders()) + + formatHeaders(this.responseHeaders) + formatHeading("Request Headers") + - formatHeaders(getRequestHeaders()); + formatHeaders(this.requestHeaders); } private String formatHeading(String heading) { diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseHeadersAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java similarity index 50% rename from spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseHeadersAssertions.java rename to spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java index 94b761163c..be32eed1b1 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseHeadersAssertions.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java @@ -15,78 +15,88 @@ */ package org.springframework.test.web.reactive.server; -import java.util.function.Consumer; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; import org.springframework.http.CacheControl; import org.springframework.http.ContentDisposition; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.util.CollectionUtils; import static org.springframework.test.util.AssertionErrors.assertEquals; +import static org.springframework.test.util.AssertionErrors.assertTrue; /** - * Assertions on specific, commonly used response headers. + * Provides methods for HTTP header assertions. * * @author Rossen Stoyanchev * @since 5.0 + * @see ResponseAssertions#header() */ -public class ResponseHeadersAssertions { +public class HeaderAssertions { - private final ExchangeActions exchangeActions; + private final ResponseAssertions resultAssertions; private final HttpHeaders headers; - ResponseHeadersAssertions(ExchangeActions actions, HttpHeaders headers) { - this.exchangeActions = actions; + public HeaderAssertions(HttpHeaders headers, ResponseAssertions resultAssertions) { + this.resultAssertions = resultAssertions; this.headers = headers; } - public ExchangeActions cacheControl(CacheControl cacheControl) { + public ResponseAssertions valueEquals(String headerName, String... values) { + List actual = this.headers.get(headerName); + assertEquals("Response header [" + headerName + "]", Arrays.asList(values), actual); + return this.resultAssertions; + } + + public ResponseAssertions valueMatches(String headerName, String pattern) { + List values = this.headers.get(headerName); + String value = CollectionUtils.isEmpty(values) ? "" : values.get(0); + boolean match = Pattern.compile(pattern).matcher(value).matches(); + String message = "Response header " + headerName + "=\'" + value + "\' does not match " + pattern; + assertTrue(message, match); + return this.resultAssertions; + } + + public ResponseAssertions cacheControlEquals(CacheControl cacheControl) { String actual = this.headers.getCacheControl(); assertEquals("Response header Cache-Control", cacheControl.getHeaderValue(), actual); - return this.exchangeActions; + return this.resultAssertions; } - public ExchangeActions contentDisposition(ContentDisposition contentDisposition) { + public ResponseAssertions contentDispositionEquals(ContentDisposition contentDisposition) { ContentDisposition actual = this.headers.getContentDisposition(); assertEquals("Response header Content-Disposition", contentDisposition, actual); - return this.exchangeActions; + return this.resultAssertions; } - public ExchangeActions contentLength(long contentLength) { + public ResponseAssertions contentLengthEquals(long contentLength) { long actual = this.headers.getContentLength(); assertEquals("Response header Content-Length", contentLength, actual); - return this.exchangeActions; + return this.resultAssertions; } - public ExchangeActions contentType(MediaType mediaType) { + public ResponseAssertions contentTypeEquals(MediaType mediaType) { MediaType actual = this.headers.getContentType(); assertEquals("Response header Content-Type", mediaType, actual); - return this.exchangeActions; + return this.resultAssertions; } - public ExchangeActions expires(int expires) { + public ResponseAssertions expiresEquals(int expires) { long actual = this.headers.getExpires(); assertEquals("Response header Expires", expires, actual); - return this.exchangeActions; + return this.resultAssertions; } - public ExchangeActions lastModified(int lastModified) { + public ResponseAssertions lastModifiedEquals(int lastModified) { long actual = this.headers.getLastModified(); assertEquals("Response header Last-Modified", lastModified, actual); - return this.exchangeActions; - } - - /** - * Custom assertions on the response headers with a {@link Consumer}. - *

Consider using statically imported methods to improve readability - * @param consumer consumer that will apply the custom action - */ - public ExchangeActions consume(Consumer consumer) { - consumer.accept(this.headers); - return this.exchangeActions; + return this.resultAssertions; } } diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ListAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ListAssertions.java deleted file mode 100644 index ea34dc356a..0000000000 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ListAssertions.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 java.util.Arrays; -import java.util.List; - -import static org.springframework.test.util.AssertionErrors.assertEquals; -import static org.springframework.test.util.AssertionErrors.assertTrue; - -/** - * Assertions on a List of values. - * - * @param the type of element values in the collection - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -public class ListAssertions extends ObjectAssertions, ListAssertions> { - - - protected ListAssertions(ExchangeActions actions, List collection, String errorPrefix) { - super(actions, collection, errorPrefix); - } - - - /** - * Assert the size of the list. - */ - public ListAssertions hasSize(int size) { - assertEquals(getErrorPrefix() + " count", size, getValue().size()); - return this; - } - - /** - * Assert that the list contains all of the given values. - */ - @SuppressWarnings("unchecked") - public ListAssertions contains(E... entities) { - Arrays.stream(entities).forEach(entity -> { - boolean result = getValue().contains(entity); - assertTrue(getErrorPrefix() + " do not contain " + entity, result); - }); - return this; - } - - /** - * Assert the list does not contain any of the given values. - */ - @SuppressWarnings("unchecked") - public ListAssertions doesNotContain(E... entities) { - Arrays.stream(entities).forEach(entity -> { - boolean result = !getValue().contains(entity); - assertTrue(getErrorPrefix() + " should not contain " + entity, result); - }); - return this; - } - -} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/LoggingExchangeConsumer.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/LoggingActions.java similarity index 60% rename from spring-test/src/main/java/org/springframework/test/web/reactive/server/LoggingExchangeConsumer.java rename to spring-test/src/main/java/org/springframework/test/web/reactive/server/LoggingActions.java index 00357191c0..7ce29c0b9f 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/LoggingExchangeConsumer.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/LoggingActions.java @@ -26,82 +26,85 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** - * Provides options for logging information about the performed exchange. + * Provides options for logging diagnostic information about the exchange. * * @author Rossen Stoyanchev * @since 5.0 + * @see ResponseAssertions#log() */ -public class LoggingExchangeConsumer { +public class LoggingActions { - private static Log logger = LogFactory.getLog(LoggingExchangeConsumer.class); + private static Log logger = LogFactory.getLog(LoggingActions.class); + + private final ResponseAssertions resultAssertions; + + private final ExchangeResult exchange; - private final ExchangeActions exchangeActions; - - - public LoggingExchangeConsumer(ExchangeActions exchangeActions) { - this.exchangeActions = exchangeActions; + public LoggingActions(ExchangeResult exchange, ResponseAssertions resultAssertions) { + this.resultAssertions = resultAssertions; + this.exchange = exchange; } /** * Log with {@link System#out}. */ - public ExchangeActions toConsole() { - System.out.println(getOutput()); - return this.exchangeActions; + public ResponseAssertions toConsole() { + return toOutputStream(System.out); } /** * Log with a given {@link OutputStream}. */ - public ExchangeActions toOutputStream(OutputStream stream) { + public ResponseAssertions toOutputStream(OutputStream stream) { return toWriter(new PrintWriter(stream, true)); } /** * Log with a given {@link Writer}. */ - public ExchangeActions toWriter(Writer writer) { + public ResponseAssertions toWriter(Writer writer) { try { writer.write(getOutput()); } catch (IOException ex) { throw new IllegalStateException("Failed to print exchange info", ex); } - return this.exchangeActions; + return this.resultAssertions; } /** * Log if TRACE level logging is enabled. */ - public ExchangeActions ifTraceEnabled() { + public ResponseAssertions ifTraceEnabled() { return doLog(Log::isTraceEnabled, Log::trace); } /** * Log if DEBUG level logging is enabled. */ - public ExchangeActions ifDebugEnabled() { + public ResponseAssertions ifDebugEnabled() { return doLog(Log::isDebugEnabled, Log::debug); } /** * Log if INFO level logging is enabled. */ - public ExchangeActions ifInfoEnabled() { + public ResponseAssertions ifInfoEnabled() { return doLog(Log::isInfoEnabled, Log::info); } - private ExchangeActions doLog(Predicate logLevelPredicate, BiConsumer logAction) { - if (logLevelPredicate.test(logger)) { - logAction.accept(logger, getOutput()); + + private ResponseAssertions doLog(Predicate predicate, BiConsumer consumer) { + if (predicate.test(logger)) { + consumer.accept(logger, getOutput()); } - return this.exchangeActions; + return this.resultAssertions; } private String getOutput() { - return this.exchangeActions.andReturn().toString(); + return this.resultAssertions.toString(); } } diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/MapAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/MapAssertions.java deleted file mode 100644 index 4cc5b03559..0000000000 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/MapAssertions.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 java.util.Arrays; -import java.util.Map; - -import static org.springframework.test.util.AssertionErrors.assertEquals; -import static org.springframework.test.util.AssertionErrors.assertTrue; - -/** - * Assertions on a map of values. - * - * @param the type of values in map - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -public class MapAssertions extends ObjectAssertions, MapAssertions> { - - - protected MapAssertions(ExchangeActions actions, Map map, String errorPrefix) { - super(actions, map, errorPrefix); - } - - - /** - * Assert the size of the map. - */ - public MapAssertions hasSize(int size) { - assertEquals(getErrorPrefix() + " count", size, getValue().size()); - return this; - } - - /** - * Assert that the map contains all of the given values. - */ - @SuppressWarnings("unchecked") - public MapAssertions contains(K key, V value) { - V actual = getValue().get(key); - assertEquals(getErrorPrefix() + " do not contain " + value, value, actual); - return this; - } - - /** - * Assert that the map contains all of the given keys. - */ - @SuppressWarnings("unchecked") - public MapAssertions containsKeys(K... keys) { - Arrays.stream(keys).forEach(key -> { - boolean result = getValue().containsKey(key); - assertTrue(getErrorPrefix() + " does not contain key " + key, result); - }); - return this; - } - - /** - * Assert that the map contains all of the given keys. - */ - @SuppressWarnings("unchecked") - public MapAssertions containsValues(V... values) { - Arrays.stream(values).forEach(value -> { - boolean result = getValue().containsValue(value); - assertTrue(getErrorPrefix() + " does not contain value " + value, result); - }); - return this; - } - - /** - * Assert the map does not contain any of the given keys. - */ - @SuppressWarnings("unchecked") - public MapAssertions doesNotContain(K... keys) { - Arrays.stream(keys).forEach(key -> { - boolean result = !getValue().containsKey(key); - assertTrue(getErrorPrefix() + " should not contain " + key, result); - }); - return this; - } - -} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/MultiValueMapEntryAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/MultiValueMapEntryAssertions.java deleted file mode 100644 index 0a085408fc..0000000000 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/MultiValueMapEntryAssertions.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 java.util.Arrays; -import java.util.List; - -import org.springframework.util.MultiValueMap; - -import static org.springframework.test.util.AssertionErrors.assertEquals; -import static org.springframework.test.util.AssertionErrors.assertTrue; -import static org.springframework.test.util.AssertionErrors.fail; - -/** - * Assertions on the values of a {@link MultiValueMap} entry. - * - * @param the type of values in the map. - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -public class MultiValueMapEntryAssertions { - - private final ExchangeActions exchangeActions; - - private final String name; - - private final MultiValueMap map; - - private final String errorMessagePrefix; - - - MultiValueMapEntryAssertions(ExchangeActions actions, String name, - MultiValueMap map, String errorMessagePrefix) { - - this.exchangeActions = actions; - this.name = name; - this.map = map; - this.errorMessagePrefix = errorMessagePrefix + " " + this.name; - } - - - /** - * The given values are equal to the actual values. - * @param values the values to match - */ - @SuppressWarnings("unchecked") - public ExchangeActions isEqualTo(V... values) { - List actual = this.map.get(this.name); - assertEquals(this.errorMessagePrefix, Arrays.asList(values), actual); - return this.exchangeActions; - } - - /** - * The list of actual values contains the given values. - * @param values the values to match - */ - @SuppressWarnings("unchecked") - public ExchangeActions hasValues(V... values) { - List actual = this.map.get(this.name); - List expected = Arrays.asList(values); - String message = getErrorMessagePrefix() + " does not contain " + expected; - assertTrue(message, actual.containsAll(expected)); - return this.exchangeActions; - } - - - // Protected methods for sub-classes - - protected ExchangeActions getExchangeActions() { - return this.exchangeActions; - } - - protected String getName() { - return this.name; - } - - protected MultiValueMap getMap() { - return this.map; - } - - protected String getErrorMessagePrefix() { - return this.errorMessagePrefix; - } - - protected V getValue(int index) { - List actualValues = getMap().get(getName()); - if (actualValues == null || index >= actualValues.size()) { - fail(getErrorMessagePrefix() + " does not have values at index[" + index + "]: " + actualValues); - } - return actualValues.get(index); - } - -} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ObjectAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ObjectAssertions.java deleted file mode 100644 index 733e775c7d..0000000000 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ObjectAssertions.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 java.time.Duration; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import org.springframework.web.reactive.function.client.ClientResponse; - -import static org.springframework.test.util.AssertionErrors.assertEquals; - -/** - * Assertions on an Object. - * - * @param the type of Object to apply assertions to - * @param assertion method return type - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -public class ObjectAssertions> { - - private final ExchangeActions exchangeActions; - - private V value; - - private final Supplier valueSupplier; - - private final String errorPrefix; - - - protected ObjectAssertions(ExchangeActions exchangeActions, V value, String errorPrefix) { - this(exchangeActions, () -> value, errorPrefix); - } - - protected ObjectAssertions(ExchangeActions exchangeActions, Supplier valueSupplier, String errorPrefix) { - this.exchangeActions = exchangeActions; - this.valueSupplier = valueSupplier; - this.errorPrefix = errorPrefix; - } - - - /** - * Assert the actual value is equal to the given expected value. - */ - public T isEqualTo(V expected) { - assertEquals(this.errorPrefix, expected, getValue()); - return self(); - } - - /** - * Assert the actual value is not equal to the expected value. - */ - public T isNotEqualTo(V expected) { - assertEquals(this.errorPrefix, expected, getValue()); - return self(); - } - - /** - * Custom assertions on the Object value with a {@link Consumer}. - *

Consider using statically imported methods to improve readability - * @param consumer consumer that will apply the custom action - */ - public ExchangeActions consume(Consumer consumer) { - consumer.accept(this.getValue()); - return this.exchangeActions; - } - - - /** - * Continue with more assertions or actions on the response. - */ - public ExchangeActions and() { - return this.exchangeActions; - } - - /** - * Return {@link ExchangeInfo} for direct access to request and response. - */ - public ExchangeInfo andReturn() { - return this.exchangeActions.andReturn(); - } - - - // Protected methods for sub-classes - - protected ExchangeActions getExchangeActions() { - return this.exchangeActions; - } - - protected ClientResponse getResponse() { - return this.exchangeActions.andReturn().getResponse(); - } - - protected Duration getTimeout() { - return this.exchangeActions.andReturn().getResponseTimeout(); - } - - protected String getErrorPrefix() { - return this.errorPrefix; - } - - protected V getValue() { - if (this.value == null) { - this.value = this.valueSupplier.get(); - } - return this.value; - } - - @SuppressWarnings("unchecked") - protected T self() { - return (T) this; - } - -} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseAssertions.java new file mode 100644 index 0000000000..19a4ccd497 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseAssertions.java @@ -0,0 +1,66 @@ +/* + * 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 static org.springframework.test.util.AssertionErrors.assertEquals; + +/** + * Assertions on an {@code ExchangeResult}. + * + *

Use {@link ExchangeResult#assertThat()} to access these assertions. + * + * @author Rossen Stoyanchev + * @since 5.0 + */ +public class ResponseAssertions { + + private final ExchangeResult exchangeResult; + + + ResponseAssertions(ExchangeResult exchangeResult) { + this.exchangeResult = exchangeResult; + } + + + /** + * Assertions on the response status. + */ + public StatusAssertions status() { + return new StatusAssertions<>(this.exchangeResult.getResponseStatus(), this); + } + + /** + * Assertions on response headers. + */ + public HeaderAssertions header() { + return new HeaderAssertions<>(this.exchangeResult.getResponseHeaders(), this); + } + + /** + * Assert the response body is equal to the given value. + */ + public void bodyEquals(T value) { + assertEquals("Response body", value, this.exchangeResult.getResponseBody()); + } + + /** + * Options for logging diagnostic information. + */ + public LoggingActions log() { + return new LoggingActions(this.exchangeResult, this); + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseBodyAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseBodyAssertions.java deleted file mode 100644 index cf01c162e4..0000000000 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseBodyAssertions.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 java.nio.ByteBuffer; -import java.time.Duration; -import java.util.Map; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import org.springframework.core.ResolvableType; -import org.springframework.web.reactive.function.client.ClientResponse; - -import static org.springframework.web.reactive.function.BodyExtractors.toMono; - -/** - * Assertions on the body of the response. - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -public class ResponseBodyAssertions { - - private final ExchangeActions exchangeActions; - - - public ResponseBodyAssertions(ExchangeActions exchangeActions) { - this.exchangeActions = exchangeActions; - } - - - /** - * Assert the response does not have any content. - */ - public ExchangeActions isEmpty() { - Flux body = getResponse().bodyToFlux(ByteBuffer.class); - StepVerifier.create(body).expectComplete().verify(getTimeout()); - return this.exchangeActions; - } - - /** - * Assert the response decoded as a Map of String key-value pairs. - */ - public MapAssertions asMap() { - ResolvableType type = ResolvableType.forClassWithGenerics(Map.class, String.class, String.class); - Mono> mono = getResponse().body(toMono(type)); - Map map = mono.block(getTimeout()); - return new MapAssertions<>(this.exchangeActions, map, "Response body map"); - } - - - private ClientResponse getResponse() { - return this.exchangeActions.andReturn().getResponse(); - } - - private Duration getTimeout() { - return this.exchangeActions.andReturn().getResponseTimeout(); - } - -} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseEntityAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseEntityAssertions.java deleted file mode 100644 index dd38377f23..0000000000 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseEntityAssertions.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 java.util.List; -import java.util.Map; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import org.springframework.core.ResolvableType; - -import static org.springframework.web.reactive.function.BodyExtractors.toFlux; -import static org.springframework.web.reactive.function.BodyExtractors.toMono; - -/** - * Assertions on the response body decoded as a single entity. - * - * @param the response entity type - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -public class ResponseEntityAssertions extends ObjectAssertions> { - - private final ResolvableType entityType; - - - ResponseEntityAssertions(ExchangeActions actions, ResolvableType entityType) { - super(actions, () -> initEntity(actions, entityType), "Response body"); - this.entityType = entityType; - } - - private static T initEntity(ExchangeActions exchangeActions, ResolvableType entityType) { - ExchangeInfo info = exchangeActions.andReturn(); - Mono mono = info.getResponse().body(toMono(entityType)); - return mono.block(info.getResponseTimeout()); - } - - - /** - * Assert the response decoded as a List of entities of the given type. - */ - public ListAssertions list() { - Flux flux = getResponse().body(toFlux(this.entityType)); - List list = flux.collectList().block(getTimeout()); - return new ListAssertions(getExchangeActions(), list, "Response entity collection"); - } - - /** - * Assert the response decoded as a Map of entities with String keys. - */ - public MapAssertions map() { - ResolvableType keyType = ResolvableType.forClass(String.class); - ResolvableType type = ResolvableType.forClassWithGenerics(Map.class, keyType, this.entityType); - Mono> mono = getResponse().body(toMono(type)); - Map map = mono.block(getTimeout()); - return new MapAssertions<>(getExchangeActions(), map, "Response entity map"); - } - - /** - * Assert the response content using a {@link StepVerifier}. - */ - public StepVerifier.FirstStep stepVerifier() { - Flux flux = getResponse().body(toFlux(this.entityType)); - return StepVerifier.create(flux); - } - -} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseHeaderAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseHeaderAssertions.java deleted file mode 100644 index 8af24339ea..0000000000 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseHeaderAssertions.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 java.util.List; - -import org.springframework.util.CollectionUtils; - -/** - * Assertions on the values of a specific header. - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -public class ResponseHeaderAssertions extends StringAssertions { - - private final List values; - - - ResponseHeaderAssertions(ExchangeActions actions, String headerName, List values) { - super(actions, initHeaderValue(values), "Response header [" + headerName + "]"); - this.values = values; - } - - private static String initHeaderValue(List values) { - return CollectionUtils.isEmpty(values) ? null : values.get(0); - } - - - public ListAssertions values() { - return new ListAssertions<>(getExchangeActions(), this.values, getErrorPrefix()); - } - -} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseStatusAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseStatusAssertions.java deleted file mode 100644 index 34772f86e5..0000000000 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ResponseStatusAssertions.java +++ /dev/null @@ -1,606 +0,0 @@ -/* - * 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 java.util.function.Consumer; - -import org.springframework.http.HttpStatus; - -import static org.springframework.test.util.AssertionErrors.assertEquals; - -/** - * Provides methods for asserting the response status. - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -@SuppressWarnings("unused") -public class ResponseStatusAssertions { - - private final ExchangeActions exchangeActions; - - private final HttpStatus httpStatus; - - - ResponseStatusAssertions(ExchangeActions actions) { - this.exchangeActions = actions; - this.httpStatus = actions.andReturn().getResponse().statusCode(); - } - - - public ExchangeActions is(int status) { - assertEquals("Response status", status, this.httpStatus.value()); - return this.exchangeActions; - } - - /** - * Assert the response status code is in the 1xx range. - */ - public ExchangeActions is1xxInformational() { - String message = "Range for response status value " + this.httpStatus; - assertEquals(message, HttpStatus.Series.INFORMATIONAL, this.httpStatus.series()); - return this.exchangeActions; - } - - /** - * Assert the response status code is in the 2xx range. - */ - public ExchangeActions is2xxSuccessful() { - String message = "Range for response status value " + this.httpStatus; - assertEquals(message, HttpStatus.Series.SUCCESSFUL, this.httpStatus.series()); - return this.exchangeActions; - } - - /** - * Assert the response status code is in the 3xx range. - */ - public ExchangeActions is3xxRedirection() { - String message = "Range for response status value " + this.httpStatus; - assertEquals(message, HttpStatus.Series.REDIRECTION, this.httpStatus.series()); - return this.exchangeActions; - } - - /** - * Assert the response status code is in the 4xx range. - */ - public ExchangeActions is4xxClientError() { - String message = "Range for response status value " + this.httpStatus; - assertEquals(message, HttpStatus.Series.CLIENT_ERROR, this.httpStatus.series()); - return this.exchangeActions; - } - - /** - * Assert the response status code is in the 5xx range. - */ - public ExchangeActions is5xxServerError() { - String message = "Range for response status value " + this.httpStatus; - assertEquals(message, HttpStatus.Series.SERVER_ERROR, this.httpStatus.series()); - return this.exchangeActions; - } - - /** - * Custom assertions on the response status with a {@link Consumer}. - *

Consider using statically imported methods to improve readability - * @param consumer consumer that will apply the custom action - */ - public ExchangeActions consume(Consumer consumer) { - consumer.accept(this.httpStatus); - return this.exchangeActions; - } - - /** - * Assert the response error message. - */ - public ExchangeActions reason(String reason) { - assertEquals("Response status reason", reason, this.httpStatus.getReasonPhrase()); - return this.exchangeActions; - } - - /** - * Assert the response status code is {@code HttpStatus.CONTINUE} (100). - */ - public ExchangeActions isContinue() { - return assertStatusIsEqualTo(HttpStatus.CONTINUE); - } - - /** - * Assert the response status code is {@code HttpStatus.SWITCHING_PROTOCOLS} (101). - */ - public ExchangeActions isSwitchingProtocols() { - return assertStatusIsEqualTo(HttpStatus.SWITCHING_PROTOCOLS); - } - - /** - * Assert the response status code is {@code HttpStatus.PROCESSING} (102). - */ - public ExchangeActions isProcessing() { - return assertStatusIsEqualTo(HttpStatus.PROCESSING); - } - - /** - * Assert the response status code is {@code HttpStatus.CHECKPOINT} (103). - */ - public ExchangeActions isCheckpoint() { - return assertStatusIsEqualTo(HttpStatus.valueOf(103)); - } - - /** - * Assert the response status code is {@code HttpStatus.OK} (200). - */ - public ExchangeActions isOk() { - return assertStatusIsEqualTo(HttpStatus.OK); - } - - /** - * Assert the response status code is {@code HttpStatus.CREATED} (201). - */ - public ExchangeActions isCreated() { - return assertStatusIsEqualTo(HttpStatus.CREATED); - } - - /** - * Assert the response status code is {@code HttpStatus.ACCEPTED} (202). - */ - public ExchangeActions isAccepted() { - return assertStatusIsEqualTo(HttpStatus.ACCEPTED); - } - - /** - * Assert the response status code is {@code HttpStatus.NON_AUTHORITATIVE_INFORMATION} (203). - */ - public ExchangeActions isNonAuthoritativeInformation() { - return assertStatusIsEqualTo(HttpStatus.NON_AUTHORITATIVE_INFORMATION); - } - - /** - * Assert the response status code is {@code HttpStatus.NO_CONTENT} (204). - */ - public ExchangeActions isNoContent() { - return assertStatusIsEqualTo(HttpStatus.NO_CONTENT); - } - - /** - * Assert the response status code is {@code HttpStatus.RESET_CONTENT} (205). - */ - public ExchangeActions isResetContent() { - return assertStatusIsEqualTo(HttpStatus.RESET_CONTENT); - } - - /** - * Assert the response status code is {@code HttpStatus.PARTIAL_CONTENT} (206). - */ - public ExchangeActions isPartialContent() { - return assertStatusIsEqualTo(HttpStatus.PARTIAL_CONTENT); - } - - /** - * Assert the response status code is {@code HttpStatus.MULTI_STATUS} (207). - */ - public ExchangeActions isMultiStatus() { - return assertStatusIsEqualTo(HttpStatus.MULTI_STATUS); - } - - /** - * Assert the response status code is {@code HttpStatus.ALREADY_REPORTED} (208). - */ - public ExchangeActions isAlreadyReported() { - return assertStatusIsEqualTo(HttpStatus.ALREADY_REPORTED); - } - - /** - * Assert the response status code is {@code HttpStatus.IM_USED} (226). - */ - public ExchangeActions isImUsed() { - return assertStatusIsEqualTo(HttpStatus.IM_USED); - } - - /** - * Assert the response status code is {@code HttpStatus.MULTIPLE_CHOICES} (300). - */ - public ExchangeActions isMultipleChoices() { - return assertStatusIsEqualTo(HttpStatus.MULTIPLE_CHOICES); - } - - /** - * Assert the response status code is {@code HttpStatus.MOVED_PERMANENTLY} (301). - */ - public ExchangeActions isMovedPermanently() { - return assertStatusIsEqualTo(HttpStatus.MOVED_PERMANENTLY); - } - - /** - * Assert the response status code is {@code HttpStatus.FOUND} (302). - */ - public ExchangeActions isFound() { - return assertStatusIsEqualTo(HttpStatus.FOUND); - } - - /** - * Assert the response status code is {@code HttpStatus.MOVED_TEMPORARILY} (302). - * @see #isFound() - * @deprecated in favor of {@link #isFound()} - */ - @Deprecated - public ExchangeActions isMovedTemporarily() { - return assertStatusIsEqualTo(HttpStatus.MOVED_TEMPORARILY); - } - - /** - * Assert the response status code is {@code HttpStatus.SEE_OTHER} (303). - */ - public ExchangeActions isSeeOther() { - return assertStatusIsEqualTo(HttpStatus.SEE_OTHER); - } - - /** - * Assert the response status code is {@code HttpStatus.NOT_MODIFIED} (304). - */ - public ExchangeActions isNotModified() { - return assertStatusIsEqualTo(HttpStatus.NOT_MODIFIED); - } - - /** - * Assert the response status code is {@code HttpStatus.USE_PROXY} (305). - * @deprecated matching the deprecation of {@code HttpStatus.USE_PROXY} - */ - @Deprecated - public ExchangeActions isUseProxy() { - return assertStatusIsEqualTo(HttpStatus.USE_PROXY); - } - - /** - * Assert the response status code is {@code HttpStatus.TEMPORARY_REDIRECT} (307). - */ - public ExchangeActions isTemporaryRedirect() { - return assertStatusIsEqualTo(HttpStatus.TEMPORARY_REDIRECT); - } - - /** - * Assert the response status code is {@code HttpStatus.PERMANENT_REDIRECT} (308). - */ - public ExchangeActions isPermanentRedirect() { - return assertStatusIsEqualTo(HttpStatus.valueOf(308)); - } - - /** - * Assert the response status code is {@code HttpStatus.BAD_REQUEST} (400). - */ - public ExchangeActions isBadRequest() { - return assertStatusIsEqualTo(HttpStatus.BAD_REQUEST); - } - - /** - * Assert the response status code is {@code HttpStatus.UNAUTHORIZED} (401). - */ - public ExchangeActions isUnauthorized() { - return assertStatusIsEqualTo(HttpStatus.UNAUTHORIZED); - } - - /** - * Assert the response status code is {@code HttpStatus.PAYMENT_REQUIRED} (402). - */ - public ExchangeActions isPaymentRequired() { - return assertStatusIsEqualTo(HttpStatus.PAYMENT_REQUIRED); - } - - /** - * Assert the response status code is {@code HttpStatus.FORBIDDEN} (403). - */ - public ExchangeActions isForbidden() { - return assertStatusIsEqualTo(HttpStatus.FORBIDDEN); - } - - /** - * Assert the response status code is {@code HttpStatus.NOT_FOUND} (404). - */ - public ExchangeActions isNotFound() { - return assertStatusIsEqualTo(HttpStatus.NOT_FOUND); - } - - /** - * Assert the response status code is {@code HttpStatus.METHOD_NOT_ALLOWED} (405). - */ - public ExchangeActions isMethodNotAllowed() { - return assertStatusIsEqualTo(HttpStatus.METHOD_NOT_ALLOWED); - } - - /** - * Assert the response status code is {@code HttpStatus.NOT_ACCEPTABLE} (406). - */ - public ExchangeActions isNotAcceptable() { - return assertStatusIsEqualTo(HttpStatus.NOT_ACCEPTABLE); - } - - /** - * Assert the response status code is {@code HttpStatus.PROXY_AUTHENTICATION_REQUIRED} (407). - */ - public ExchangeActions isProxyAuthenticationRequired() { - return assertStatusIsEqualTo(HttpStatus.PROXY_AUTHENTICATION_REQUIRED); - } - - /** - * Assert the response status code is {@code HttpStatus.REQUEST_TIMEOUT} (408). - */ - public ExchangeActions isRequestTimeout() { - return assertStatusIsEqualTo(HttpStatus.REQUEST_TIMEOUT); - } - - /** - * Assert the response status code is {@code HttpStatus.CONFLICT} (409). - */ - public ExchangeActions isConflict() { - return assertStatusIsEqualTo(HttpStatus.CONFLICT); - } - - /** - * Assert the response status code is {@code HttpStatus.GONE} (410). - */ - public ExchangeActions isGone() { - return assertStatusIsEqualTo(HttpStatus.GONE); - } - - /** - * Assert the response status code is {@code HttpStatus.LENGTH_REQUIRED} (411). - */ - public ExchangeActions isLengthRequired() { - return assertStatusIsEqualTo(HttpStatus.LENGTH_REQUIRED); - } - - /** - * Assert the response status code is {@code HttpStatus.PRECONDITION_FAILED} (412). - */ - public ExchangeActions isPreconditionFailed() { - return assertStatusIsEqualTo(HttpStatus.PRECONDITION_FAILED); - } - - /** - * Assert the response status code is {@code HttpStatus.PAYLOAD_TOO_LARGE} (413). - * @since 4.1 - */ - public ExchangeActions isPayloadTooLarge() { - return assertStatusIsEqualTo(HttpStatus.PAYLOAD_TOO_LARGE); - } - - /** - * Assert the response status code is {@code HttpStatus.REQUEST_ENTITY_TOO_LARGE} (413). - * @deprecated matching the deprecation of {@code HttpStatus.REQUEST_ENTITY_TOO_LARGE} - * @see #isPayloadTooLarge() - */ - @Deprecated - public ExchangeActions isRequestEntityTooLarge() { - return assertStatusIsEqualTo(HttpStatus.REQUEST_ENTITY_TOO_LARGE); - } - - /** - * Assert the response status code is {@code HttpStatus.REQUEST_URI_TOO_LONG} (414). - * @since 4.1 - */ - public ExchangeActions isUriTooLong() { - return assertStatusIsEqualTo(HttpStatus.URI_TOO_LONG); - } - - /** - * Assert the response status code is {@code HttpStatus.REQUEST_URI_TOO_LONG} (414). - * @deprecated matching the deprecation of {@code HttpStatus.REQUEST_URI_TOO_LONG} - * @see #isUriTooLong() - */ - @Deprecated - public ExchangeActions isRequestUriTooLong() { - return assertStatusIsEqualTo(HttpStatus.REQUEST_URI_TOO_LONG); - } - - /** - * Assert the response status code is {@code HttpStatus.UNSUPPORTED_MEDIA_TYPE} (415). - */ - public ExchangeActions isUnsupportedMediaType() { - return assertStatusIsEqualTo(HttpStatus.UNSUPPORTED_MEDIA_TYPE); - } - - /** - * Assert the response status code is {@code HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE} (416). - */ - public ExchangeActions isRequestedRangeNotSatisfiable() { - return assertStatusIsEqualTo(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE); - } - - /** - * Assert the response status code is {@code HttpStatus.EXPECTATION_FAILED} (417). - */ - public ExchangeActions isExpectationFailed() { - return assertStatusIsEqualTo(HttpStatus.EXPECTATION_FAILED); - } - - /** - * Assert the response status code is {@code HttpStatus.I_AM_A_TEAPOT} (418). - */ - public ExchangeActions isIAmATeapot() { - return assertStatusIsEqualTo(HttpStatus.valueOf(418)); - } - - /** - * Assert the response status code is {@code HttpStatus.INSUFFICIENT_SPACE_ON_RESOURCE} (419). - * @deprecated matching the deprecation of {@code HttpStatus.INSUFFICIENT_SPACE_ON_RESOURCE} - */ - @Deprecated - public ExchangeActions isInsufficientSpaceOnResource() { - return assertStatusIsEqualTo(HttpStatus.INSUFFICIENT_SPACE_ON_RESOURCE); - } - - /** - * Assert the response status code is {@code HttpStatus.METHOD_FAILURE} (420). - * @deprecated matching the deprecation of {@code HttpStatus.METHOD_FAILURE} - */ - @Deprecated - public ExchangeActions isMethodFailure() { - return assertStatusIsEqualTo(HttpStatus.METHOD_FAILURE); - } - - /** - * Assert the response status code is {@code HttpStatus.DESTINATION_LOCKED} (421). - * @deprecated matching the deprecation of {@code HttpStatus.DESTINATION_LOCKED} - */ - @Deprecated - public ExchangeActions isDestinationLocked() { - return assertStatusIsEqualTo(HttpStatus.DESTINATION_LOCKED); - } - - /** - * Assert the response status code is {@code HttpStatus.UNPROCESSABLE_ENTITY} (422). - */ - public ExchangeActions isUnprocessableEntity() { - return assertStatusIsEqualTo(HttpStatus.UNPROCESSABLE_ENTITY); - } - - /** - * Assert the response status code is {@code HttpStatus.LOCKED} (423). - */ - public ExchangeActions isLocked() { - return assertStatusIsEqualTo(HttpStatus.LOCKED); - } - - /** - * Assert the response status code is {@code HttpStatus.FAILED_DEPENDENCY} (424). - */ - public ExchangeActions isFailedDependency() { - return assertStatusIsEqualTo(HttpStatus.FAILED_DEPENDENCY); - } - - /** - * Assert the response status code is {@code HttpStatus.UPGRADE_REQUIRED} (426). - */ - public ExchangeActions isUpgradeRequired() { - return assertStatusIsEqualTo(HttpStatus.UPGRADE_REQUIRED); - } - - /** - * Assert the response status code is {@code HttpStatus.PRECONDITION_REQUIRED} (428). - */ - public ExchangeActions isPreconditionRequired() { - return assertStatusIsEqualTo(HttpStatus.valueOf(428)); - } - - /** - * Assert the response status code is {@code HttpStatus.TOO_MANY_REQUESTS} (429). - */ - public ExchangeActions isTooManyRequests() { - return assertStatusIsEqualTo(HttpStatus.valueOf(429)); - } - - /** - * Assert the response status code is {@code HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE} (431). - */ - public ExchangeActions isRequestHeaderFieldsTooLarge() { - return assertStatusIsEqualTo(HttpStatus.valueOf(431)); - } - - /** - * Assert the response status code is {@code HttpStatus.UNAVAILABLE_FOR_LEGAL_REASONS} (451). - * @since 4.3 - */ - public ExchangeActions isUnavailableForLegalReasons() { - return assertStatusIsEqualTo(HttpStatus.valueOf(451)); - } - - /** - * Assert the response status code is {@code HttpStatus.INTERNAL_SERVER_ERROR} (500). - */ - public ExchangeActions isInternalServerError() { - return assertStatusIsEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - } - - /** - * Assert the response status code is {@code HttpStatus.NOT_IMPLEMENTED} (501). - */ - public ExchangeActions isNotImplemented() { - return assertStatusIsEqualTo(HttpStatus.NOT_IMPLEMENTED); - } - - /** - * Assert the response status code is {@code HttpStatus.BAD_GATEWAY} (502). - */ - public ExchangeActions isBadGateway() { - return assertStatusIsEqualTo(HttpStatus.BAD_GATEWAY); - } - - /** - * Assert the response status code is {@code HttpStatus.SERVICE_UNAVAILABLE} (503). - */ - public ExchangeActions isServiceUnavailable() { - return assertStatusIsEqualTo(HttpStatus.SERVICE_UNAVAILABLE); - } - - /** - * Assert the response status code is {@code HttpStatus.GATEWAY_TIMEOUT} (504). - */ - public ExchangeActions isGatewayTimeout() { - return assertStatusIsEqualTo(HttpStatus.GATEWAY_TIMEOUT); - } - - /** - * Assert the response status code is {@code HttpStatus.HTTP_VERSION_NOT_SUPPORTED} (505). - */ - public ExchangeActions isHttpVersionNotSupported() { - return assertStatusIsEqualTo(HttpStatus.HTTP_VERSION_NOT_SUPPORTED); - } - - /** - * Assert the response status code is {@code HttpStatus.VARIANT_ALSO_NEGOTIATES} (506). - */ - public ExchangeActions isVariantAlsoNegotiates() { - return assertStatusIsEqualTo(HttpStatus.VARIANT_ALSO_NEGOTIATES); - } - - /** - * Assert the response status code is {@code HttpStatus.INSUFFICIENT_STORAGE} (507). - */ - public ExchangeActions isInsufficientStorage() { - return assertStatusIsEqualTo(HttpStatus.INSUFFICIENT_STORAGE); - } - - /** - * Assert the response status code is {@code HttpStatus.LOOP_DETECTED} (508). - */ - public ExchangeActions isLoopDetected() { - return assertStatusIsEqualTo(HttpStatus.LOOP_DETECTED); - } - - /** - * Assert the response status code is {@code HttpStatus.BANDWIDTH_LIMIT_EXCEEDED} (509). - */ - public ExchangeActions isBandwidthLimitExceeded() { - return assertStatusIsEqualTo(HttpStatus.valueOf(509)); - } - - /** - * Assert the response status code is {@code HttpStatus.NOT_EXTENDED} (510). - */ - public ExchangeActions isNotExtended() { - return assertStatusIsEqualTo(HttpStatus.NOT_EXTENDED); - } - - /** - * Assert the response status code is {@code HttpStatus.NETWORK_AUTHENTICATION_REQUIRED} (511). - */ - public ExchangeActions isNetworkAuthenticationRequired() { - return assertStatusIsEqualTo(HttpStatus.valueOf(511)); - } - - private ExchangeActions assertStatusIsEqualTo(final HttpStatus status) { - assertEquals("Status", status, this.httpStatus); - return this.exchangeActions; - } - -} 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 new file mode 100644 index 0000000000..8f7d04cbf5 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/StatusAssertions.java @@ -0,0 +1,200 @@ +/* + * 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 org.springframework.http.HttpStatus; + +import static org.springframework.test.util.AssertionErrors.assertEquals; + +/** + * Assertions on the status of a response. + * + * @author Rossen Stoyanchev + * @since 5.0 + * @see ResponseAssertions#status() + */ +@SuppressWarnings("unused") +public class StatusAssertions { + + private final ResponseAssertions resultAssertions; + + private final HttpStatus httpStatus; + + + StatusAssertions(HttpStatus status, ResponseAssertions exchangeActions) { + this.resultAssertions = exchangeActions; + this.httpStatus = status; + } + + + /** + * Assert the response status as an {@link HttpStatus}. + */ + public ResponseAssertions isEqualTo(HttpStatus status) { + assertEquals("Response status", status, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status as an integer. + */ + public ResponseAssertions isEqualTo(int status) { + assertEquals("Response status", status, this.httpStatus.value()); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.OK} (200). + */ + public ResponseAssertions isOk() { + assertEquals("Status", HttpStatus.OK, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.CREATED} (201). + */ + public ResponseAssertions isCreated() { + assertEquals("Status", HttpStatus.CREATED, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.ACCEPTED} (202). + */ + public ResponseAssertions isAccepted() { + assertEquals("Status", HttpStatus.ACCEPTED, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.NO_CONTENT} (204). + */ + public ResponseAssertions isNoContent() { + assertEquals("Status", HttpStatus.NO_CONTENT, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.FOUND} (302). + */ + public ResponseAssertions isFound() { + assertEquals("Status", HttpStatus.FOUND, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.SEE_OTHER} (303). + */ + public ResponseAssertions isSeeOther() { + assertEquals("Status", HttpStatus.SEE_OTHER, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.NOT_MODIFIED} (304). + */ + public ResponseAssertions isNotModified() { + assertEquals("Status", HttpStatus.NOT_MODIFIED, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.TEMPORARY_REDIRECT} (307). + */ + public ResponseAssertions isTemporaryRedirect() { + assertEquals("Status", HttpStatus.TEMPORARY_REDIRECT, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.PERMANENT_REDIRECT} (308). + */ + public ResponseAssertions isPermanentRedirect() { + assertEquals("Status", HttpStatus.PERMANENT_REDIRECT, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.BAD_REQUEST} (400). + */ + public ResponseAssertions isBadRequest() { + assertEquals("Status", HttpStatus.BAD_REQUEST, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response status code is {@code HttpStatus.NOT_FOUND} (404). + */ + public ResponseAssertions isNotFound() { + assertEquals("Status", HttpStatus.NOT_FOUND, this.httpStatus); + return this.resultAssertions; + } + + /** + * Assert the response error message. + */ + public ResponseAssertions reasonEquals(String reason) { + assertEquals("Response status reason", reason, this.httpStatus.getReasonPhrase()); + return this.resultAssertions; + } + + /** + * Assert the response status code is in the 1xx range. + */ + public ResponseAssertions is1xxInformational() { + String message = "Range for response status value " + this.httpStatus; + assertEquals(message, HttpStatus.Series.INFORMATIONAL, this.httpStatus.series()); + return this.resultAssertions; + } + + /** + * Assert the response status code is in the 2xx range. + */ + public ResponseAssertions is2xxSuccessful() { + String message = "Range for response status value " + this.httpStatus; + assertEquals(message, HttpStatus.Series.SUCCESSFUL, this.httpStatus.series()); + return this.resultAssertions; + } + + /** + * Assert the response status code is in the 3xx range. + */ + public ResponseAssertions is3xxRedirection() { + String message = "Range for response status value " + this.httpStatus; + assertEquals(message, HttpStatus.Series.REDIRECTION, this.httpStatus.series()); + return this.resultAssertions; + } + + /** + * Assert the response status code is in the 4xx range. + */ + public ResponseAssertions is4xxClientError() { + String message = "Range for response status value " + this.httpStatus; + assertEquals(message, HttpStatus.Series.CLIENT_ERROR, this.httpStatus.series()); + return this.resultAssertions; + } + + /** + * Assert the response status code is in the 5xx range. + */ + public ResponseAssertions is5xxServerError() { + String message = "Range for response status value " + this.httpStatus; + assertEquals(message, HttpStatus.Series.SERVER_ERROR, this.httpStatus.series()); + return this.resultAssertions; + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/StringAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/StringAssertions.java deleted file mode 100644 index cce097b458..0000000000 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/StringAssertions.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 java.util.Arrays; -import java.util.regex.Pattern; - -import static org.springframework.test.util.AssertionErrors.assertTrue; - -/** - * Assertions on a String value. - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -public class StringAssertions extends ObjectAssertions { - - - public StringAssertions(ExchangeActions exchangeActions, String value, String errorPrefix) { - super(exchangeActions, value, errorPrefix); - } - - - /** - * The value {@link String#contains contains} the given sub-strings. - * @param values the values to match - */ - public StringAssertions contains(CharSequence... values) { - Arrays.stream(values).forEach(value -> { - String message = getErrorPrefix() + " does not contain " + value; - assertTrue(message, getValue().contains(value)); - }); - return this; - } - - /** - * The value does not {@link String#contains contain} the given sub-strings. - * @param values the values to match - */ - public StringAssertions doesNotContain(CharSequence... values) { - Arrays.stream(values).forEach(value -> { - String message = getErrorPrefix() + " contains " + value + " but shouldn't"; - assertTrue(message, !getValue().contains(value)); - }); - return this; - } - - /** - * The value matches the given regex pattern value. - * @param pattern the values to be compiled with {@link Pattern} - */ - public StringAssertions matches(String pattern) { - boolean match = Pattern.compile(pattern).matcher(getValue()).matches(); - String message = getErrorPrefix() + " with value " + getValue() + " does not match " + pattern; - assertTrue(message, match); - return this; - } - -} 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 fff068c3a1..123e1e018d 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,8 +25,10 @@ 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.format.FormatterRegistry; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -330,12 +332,13 @@ public interface WebTestClient { /** - * Contract for specifying the URI for a request. + * Specification for providing the URI of a request. */ interface UriSpec { /** * Specify the URI using an absolute, fully constructed {@link URI}. + * @return spec to add headers or perform the exchange */ HeaderSpec uri(URI uri); @@ -343,6 +346,7 @@ public interface WebTestClient { * Specify the URI for the request using a URI template and URI variables. * If a {@link UriBuilderFactory} was configured for the client (e.g. * with a base URI) it will be used to expand the URI template. + * @return spec to add headers or perform the exchange */ HeaderSpec uri(String uri, Object... uriVariables); @@ -350,19 +354,21 @@ public interface WebTestClient { * Specify the URI for the request using a URI template and URI variables. * If a {@link UriBuilderFactory} was configured for the client (e.g. * with a base URI) it will be used to expand the URI template. + * @return spec to add headers or perform the exchange */ HeaderSpec uri(String uri, Map uriVariables); /** * Build the URI for the request with a {@link UriBuilder} obtained * through the {@link UriBuilderFactory} configured for this client. + * @return spec to add headers or perform the exchange */ HeaderSpec uri(Function uriFunction); } /** - * Contract for specifying request headers leading up to the exchange. + * Specification for adding request headers and performing an exchange. */ interface HeaderSpec { @@ -370,7 +376,7 @@ public interface WebTestClient { * Set the list of acceptable {@linkplain MediaType media types}, as * specified by the {@code Accept} header. * @param acceptableMediaTypes the acceptable media types - * @return this builder + * @return the same instance */ HeaderSpec accept(MediaType... acceptableMediaTypes); @@ -378,7 +384,7 @@ public interface WebTestClient { * Set the list of acceptable {@linkplain Charset charsets}, as specified * by the {@code Accept-Charset} header. * @param acceptableCharsets the acceptable charsets - * @return this builder + * @return the same instance */ HeaderSpec acceptCharset(Charset... acceptableCharsets); @@ -386,7 +392,7 @@ public interface WebTestClient { * Set the length of the body in bytes, as specified by the * {@code Content-Length} header. * @param contentLength the content length - * @return this builder + * @return the same instance * @see HttpHeaders#setContentLength(long) */ HeaderSpec contentLength(long contentLength); @@ -395,7 +401,7 @@ public interface WebTestClient { * Set the {@linkplain MediaType media type} of the body, as specified * by the {@code Content-Type} header. * @param contentType the content type - * @return this builder + * @return the same instance * @see HttpHeaders#setContentType(MediaType) */ HeaderSpec contentType(MediaType contentType); @@ -404,7 +410,7 @@ public interface WebTestClient { * Add a cookie with the given name and value. * @param name the cookie name * @param value the cookie value - * @return this builder + * @return the same instance */ HeaderSpec cookie(String name, String value); @@ -412,7 +418,7 @@ public interface WebTestClient { * Copy the given cookies into the entity's cookies map. * * @param cookies the existing cookies to copy from - * @return this builder + * @return the same instance */ HeaderSpec cookies(MultiValueMap cookies); @@ -421,14 +427,14 @@ public interface WebTestClient { *

The date should be specified as the number of milliseconds since * January 1, 1970 GMT. * @param ifModifiedSince the new value of the header - * @return this builder + * @return the same instance */ HeaderSpec ifModifiedSince(ZonedDateTime ifModifiedSince); /** * Set the values of the {@code If-None-Match} header. * @param ifNoneMatches the new value of the header - * @return this builder + * @return the same instance */ HeaderSpec ifNoneMatch(String... ifNoneMatches); @@ -436,42 +442,112 @@ public interface WebTestClient { * Add the given, single header value under the given name. * @param headerName the header name * @param headerValues the header value(s) - * @return this builder + * @return the same instance */ HeaderSpec header(String headerName, String... headerValues); /** * Copy the given headers into the entity's headers map. * @param headers the existing headers to copy from - * @return this builder + * @return the same instance */ HeaderSpec headers(HttpHeaders headers); /** - * Perform the request without a request body. - * @return options for asserting the response with + * Perform the exchange without a request body. + * @return spec for decoding the response */ - ExchangeActions exchange(); + ResponseSpec exchange(); /** - * Set the body of the request to the given {@code BodyInserter} and - * perform the request. - * @param inserter the {@code BodyInserter} that writes to the request - * @param the type contained in the body - * @return options for asserting the response with + * Perform the exchange with the body for the request populated using + * a {@link BodyInserter}. + * @param inserter the inserter + * @param the body type, or the the element type (for a stream) + * @return spec for decoding the response + * @see org.springframework.web.reactive.function.BodyInserters */ - ExchangeActions exchange(BodyInserter inserter); + ResponseSpec exchange(BodyInserter inserter); /** - * Set the body of the request to the given {@code Publisher} and - * perform the request. - * @param publisher the {@code Publisher} to write to the request + * Perform the exchange and use the given {@code Publisher} for the + * request body. + * @param publisher the request body data * @param elementClass the class of elements contained in the publisher * @param the type of the elements contained in the publisher * @param the type of the {@code Publisher} - * @return options for asserting the response with + * @return spec for decoding the response */ - > ExchangeActions exchange(S publisher, Class elementClass); + > ResponseSpec exchange(S publisher, Class elementClass); + } + + /** + * Specification for decoding the response of an exchange. + */ + interface ResponseSpec { + + /** + * Decode the response as a single entity of the given type. + * @param entityClass the entity type + * @param the type of entity + * @return container for the result of the exchange + */ + ExchangeResult decodeEntity(Class entityClass); + + /** + * Alternative to {@link #decodeEntity(Class)} useful for entity + * types with generics. + * @param entityType the type of entity + * @param the type of entity + * @return container for the result of the exchange + * @see ResolvableType#forClassWithGenerics(Class, Class[]) + */ + ExchangeResult decodeEntity(ResolvableType entityType); + + /** + * Decode the response as a finite stream of the given element type and + * collect the items into a list. + * @param elementClass the list element type + * @param the type of list elements + * @return container for the result of the exchange + */ + ExchangeResult> decodeAndCollect(Class elementClass); + + /** + * Alternative to {@link #decodeAndCollect(Class)} useful for element + * types with generics. + * @param elementType the list element type + * @param the type of list elements + * @return container for the result of the exchange + * @see ResolvableType#forClassWithGenerics(Class, Class[]) + */ + ExchangeResult> decodeAndCollect(ResolvableType elementType); + + /** + * Turn the response stream of byte buffers into a stream of Objects of + * the given type. + * @param elementClass the stream element type + * @param the type of stream elements + * @return container for the result of the exchange + */ + ExchangeResult> decodeFlux(Class elementClass); + + /** + * Alternative to {@link #decodeFlux(Class)} useful for element types + * with generics. + * @param elementType the stream element type + * @param the type of stream elements + * @return container for the result of the exchange + * @see ResolvableType#forClassWithGenerics(Class, Class[]) + */ + ExchangeResult> decodeFlux(ResolvableType elementType); + + /** + * Consume the response and verify there is no content. + * @return container for the result of the exchange + */ + ExchangeResult expectNoBody(); + } } diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ErrorTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ErrorTests.java index 5a2fdf6457..987fcf0edd 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ErrorTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ErrorTests.java @@ -18,6 +18,7 @@ package org.springframework.test.web.reactive.server.samples; import org.junit.Before; import org.junit.Test; +import org.springframework.http.HttpStatus; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -43,14 +44,18 @@ public class ErrorTests { public void notFound() throws Exception { this.client.get().uri("/invalid") .exchange() - .assertStatus().isNotFound(); + .expectNoBody() + .assertThat() + .status().isNotFound(); } @Test public void serverException() throws Exception { this.client.get().uri("/server-error") .exchange() - .assertStatus().isInternalServerError(); + .expectNoBody() + .assertThat() + .status().isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); } diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/HeaderTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/HeaderTests.java index 9cbe8b8f15..8ed6824b4a 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/HeaderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/HeaderTests.java @@ -15,8 +15,6 @@ */ package org.springframework.test.web.reactive.server.samples; -import java.util.Arrays; - import org.junit.Before; import org.junit.Test; @@ -27,8 +25,6 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import static org.junit.Assert.assertEquals; - /** * Tests with custom headers. * @@ -53,17 +49,20 @@ public class HeaderTests { public void requestResponseHeaderPair() throws Exception { this.client.get().uri("/request-response-pair").header("h1", "in") .exchange() - .assertStatus().isOk() - .assertHeader("h1").isEqualTo("in-out"); + .expectNoBody() + .assertThat() + .status().isOk() + .header().valueEquals("h1", "in-out"); } @Test - public void headerConsumer() throws Exception { + public void headerMultivalue() throws Exception { this.client.get().uri("/multivalue") .exchange() - .assertStatus().isOk() - .assertHeader("h1").consume(value -> assertEquals("v1", value)) - .assertHeader("h1").values().consume(values -> assertEquals(Arrays.asList("v1", "v2", "v3"), values)); + .expectNoBody() + .assertThat() + .status().isOk() + .header().valueEquals("h1", "v1", "v2", "v3"); } 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 101cd12d00..c3f0e598c8 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 @@ -16,6 +16,7 @@ package org.springframework.test.web.reactive.server.samples; import java.net.URI; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; @@ -25,9 +26,12 @@ import org.junit.Before; import org.junit.Test; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import org.springframework.core.ResolvableType; 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.WebTestClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -37,7 +41,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import static org.hamcrest.CoreMatchers.endsWith; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.springframework.http.MediaType.TEXT_EVENT_STREAM; @@ -62,37 +65,54 @@ public class ResponseEntityTests { public void entity() throws Exception { this.client.get().uri("/persons/John") .exchange() - .assertStatus().isOk() - .assertHeaders().contentType(MediaType.APPLICATION_JSON_UTF8) - .assertEntity(Person.class).isEqualTo(new Person("John")); + .decodeEntity(Person.class) + .assertThat() + .status().isOk() + .header().contentTypeEquals(MediaType.APPLICATION_JSON_UTF8) + .bodyEquals(new Person("John")); } @Test public void entityList() throws Exception { this.client.get().uri("/persons") .exchange() - .assertStatus().isOk() - .assertHeaders().contentType(MediaType.APPLICATION_JSON_UTF8) - .assertEntity(Person.class).list() - .hasSize(3) - .contains(new Person("Jane"), new Person("Jason"), new Person("John")); + .decodeAndCollect(Person.class) + .assertThat() + .status().isOk() + .header().contentTypeEquals(MediaType.APPLICATION_JSON_UTF8) + .bodyEquals(Arrays.asList(new Person("Jane"), new Person("Jason"), new Person("John"))); } @Test public void entityMap() throws Exception { + + Map map = new LinkedHashMap<>(); + map.put("Jane", new Person("Jane")); + map.put("Jason", new Person("Jason")); + map.put("John", new Person("John")); + this.client.get().uri("/persons?map=true") .exchange() - .assertStatus().isOk() - .assertEntity(Person.class).map().hasSize(3).containsKeys("Jane", "Jason", "John"); + .decodeEntity(ResolvableType.forClassWithGenerics(Map.class, String.class, Person.class)) + .assertThat() + .status().isOk() + .bodyEquals(map); } @Test public void entityStream() throws Exception { - this.client.get().uri("/persons").accept(TEXT_EVENT_STREAM) + + ExchangeResult> result = this.client.get() + .uri("/persons") + .accept(TEXT_EVENT_STREAM) .exchange() - .assertStatus().isOk() - .assertHeaders().contentType(TEXT_EVENT_STREAM) - .assertEntity(Person.class).stepVerifier() + .decodeFlux(Person.class); + + result.assertThat() + .status().isOk() + .header().contentTypeEquals(TEXT_EVENT_STREAM); + + StepVerifier.create(result.getResponseBody()) .expectNext(new Person("N0"), new Person("N1"), new Person("N2")) .expectNextCount(4) .consumeNextWith(person -> assertThat(person.getName(), endsWith("7"))) @@ -104,17 +124,10 @@ public class ResponseEntityTests { public void postEntity() throws Exception { this.client.post().uri("/persons") .exchange(Mono.just(new Person("John")), Person.class) - .assertStatus().isCreated() - .assertHeader("location").isEqualTo("/persons/John").and() - .assertBody().isEmpty(); - } - - @Test - public void entityConsumer() throws Exception { - this.client.get().uri("/persons/John") - .exchange() - .assertStatus().isOk() - .assertEntity(Person.class).consume(p -> assertEquals(new Person("John"), p)); + .expectNoBody() + .assertThat() + .status().isCreated() + .header().valueEquals("location", "/persons/John"); } diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/ApplicationContextTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/ApplicationContextTests.java index 9cfefa2911..3bf3fc0475 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/ApplicationContextTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/ApplicationContextTests.java @@ -51,8 +51,10 @@ public class ApplicationContextTests { public void test() throws Exception { this.client.get().uri("/test") .exchange() - .assertStatus().isOk() - .assertEntity(String .class).isEqualTo("It works!"); + .decodeEntity(String.class) + .assertThat() + .status().isOk() + .bodyEquals("It works!"); } diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/ControllerTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/ControllerTests.java index 542e6aacb6..a39a558d38 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/ControllerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/ControllerTests.java @@ -43,8 +43,10 @@ public class ControllerTests { public void test() throws Exception { this.client.get().uri("/test") .exchange() - .assertStatus().isOk() - .assertEntity(String .class).isEqualTo("It works!"); + .decodeEntity(String.class) + .assertThat() + .status().isOk() + .bodyEquals("It works!"); } diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/HttpServerTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/HttpServerTests.java index aede9ed651..ef3de7f936 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/HttpServerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/HttpServerTests.java @@ -68,8 +68,10 @@ public class HttpServerTests { public void test() throws Exception { this.client.get().uri("/test") .exchange() - .assertStatus().isOk() - .assertEntity(String .class).isEqualTo("It works!"); + .decodeEntity(String.class) + .assertThat() + .status().isOk() + .bodyEquals("It works!"); } } diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/RouterFunctionTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/RouterFunctionTests.java index 49c1dd6cf8..54161b4377 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/RouterFunctionTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/bind/RouterFunctionTests.java @@ -49,8 +49,10 @@ public class RouterFunctionTests { public void test() throws Exception { this.testClient.get().uri("/test") .exchange() - .assertStatus().isOk() - .assertEntity(String .class).isEqualTo("It works!"); + .decodeEntity(String.class) + .assertThat() + .status().isOk() + .bodyEquals("It works!"); } }