12 changed files with 995 additions and 5 deletions
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
/* |
||||
* Copyright 2002-present 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 |
||||
* |
||||
* https://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.assertj; |
||||
|
||||
|
||||
import org.springframework.test.web.reactive.server.ExchangeResult; |
||||
|
||||
/** |
||||
* Default implementation of {@link WebTestClientResponse}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 7.0 |
||||
*/ |
||||
final class DefaultWebTestClientResponse implements WebTestClientResponse { |
||||
|
||||
private final ExchangeResult exchangeResult; |
||||
|
||||
|
||||
DefaultWebTestClientResponse(ExchangeResult exchangeResult) { |
||||
this.exchangeResult = exchangeResult; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public ExchangeResult getExchangeResult() { |
||||
return this.exchangeResult; |
||||
} |
||||
|
||||
/** |
||||
* Use AssertJ's {@link org.assertj.core.api.Assertions#assertThat assertThat} instead. |
||||
*/ |
||||
@Override |
||||
public WebTestClientResponseAssert assertThat() { |
||||
return new WebTestClientResponseAssert(this); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,163 @@
@@ -0,0 +1,163 @@
|
||||
/* |
||||
* Copyright 2002-present 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 |
||||
* |
||||
* https://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.assertj; |
||||
|
||||
import java.time.Duration; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
import java.util.function.Consumer; |
||||
|
||||
import org.assertj.core.api.AbstractMapAssert; |
||||
import org.assertj.core.api.Assertions; |
||||
|
||||
import org.springframework.http.ResponseCookie; |
||||
|
||||
/** |
||||
* AssertJ {@linkplain org.assertj.core.api.Assert assertions} that can be applied |
||||
* to {@link ResponseCookie cookies}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 7.0 |
||||
*/ |
||||
public class ResponseCookieMapAssert |
||||
extends AbstractMapAssert<ResponseCookieMapAssert, Map<String, ResponseCookie>, String, ResponseCookie> { |
||||
|
||||
|
||||
public ResponseCookieMapAssert(ResponseCookie[] actual) { |
||||
super(toMap(actual), ResponseCookieMapAssert.class); |
||||
as("Cookies"); |
||||
} |
||||
|
||||
private static Map<String, ResponseCookie> toMap(ResponseCookie[] cookies) { |
||||
Map<String, ResponseCookie> map = new LinkedHashMap<>(); |
||||
for (ResponseCookie cookie : cookies) { |
||||
map.putIfAbsent(cookie.getName(), cookie); |
||||
} |
||||
return map; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Verify that the actual cookies contain a cookie with the given {@code name}. |
||||
* @param name the name of an expected cookie |
||||
* @see #containsKey |
||||
*/ |
||||
public ResponseCookieMapAssert containsCookie(String name) { |
||||
return containsKey(name); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual cookies contain cookies with the given {@code names}. |
||||
* @param names the names of expected cookies |
||||
* @see #containsKeys |
||||
*/ |
||||
public ResponseCookieMapAssert containsCookies(String... names) { |
||||
return containsKeys(names); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual cookies do not contain a cookie with the given |
||||
* {@code name}. |
||||
* @param name the name of a cookie that should not be present |
||||
* @see #doesNotContainKey |
||||
*/ |
||||
public ResponseCookieMapAssert doesNotContainCookie(String name) { |
||||
return doesNotContainKey(name); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual cookies do not contain any cookies with the given |
||||
* {@code names}. |
||||
* @param names the names of cookies that should not be present |
||||
* @see #doesNotContainKeys |
||||
*/ |
||||
public ResponseCookieMapAssert doesNotContainCookies(String... names) { |
||||
return doesNotContainKeys(names); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual cookies contain a cookie with the given {@code name} |
||||
* that satisfies the given {@code cookieRequirements}. |
||||
* @param name the name of an expected cookie |
||||
* @param cookieRequirements the requirements for the cookie |
||||
*/ |
||||
public ResponseCookieMapAssert hasCookieSatisfying(String name, Consumer<ResponseCookie> cookieRequirements) { |
||||
return hasEntrySatisfying(name, cookieRequirements); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual cookies contain a cookie with the given {@code name} |
||||
* whose {@linkplain ResponseCookie#getValue() value} is equal to the given one. |
||||
* @param name the name of the cookie |
||||
* @param expected the expected value of the cookie |
||||
*/ |
||||
public ResponseCookieMapAssert hasValue(String name, String expected) { |
||||
return hasCookieSatisfying(name, cookie -> Assertions.assertThat(cookie.getValue()).isEqualTo(expected)); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual cookies contain a cookie with the given {@code name} |
||||
* whose {@linkplain ResponseCookie#getMaxAge() max age} is equal to the given one. |
||||
* @param name the name of the cookie |
||||
* @param expected the expected max age of the cookie |
||||
*/ |
||||
public ResponseCookieMapAssert hasMaxAge(String name, Duration expected) { |
||||
return hasCookieSatisfying(name, cookie -> Assertions.assertThat(cookie.getMaxAge()).isEqualTo(expected)); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual cookies contain a cookie with the given {@code name} |
||||
* whose {@linkplain ResponseCookie#getPath() path} is equal to the given one. |
||||
* @param name the name of the cookie |
||||
* @param expected the expected path of the cookie |
||||
*/ |
||||
public ResponseCookieMapAssert hasPath(String name, String expected) { |
||||
return hasCookieSatisfying(name, cookie -> Assertions.assertThat(cookie.getPath()).isEqualTo(expected)); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual cookies contain a cookie with the given {@code name} |
||||
* whose {@linkplain ResponseCookie#getDomain() domain} is equal to the given one. |
||||
* @param name the name of the cookie |
||||
* @param expected the expected domain of the cookie |
||||
*/ |
||||
public ResponseCookieMapAssert hasDomain(String name, String expected) { |
||||
return hasCookieSatisfying(name, cookie -> Assertions.assertThat(cookie.getDomain()).isEqualTo(expected)); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual cookies contain a cookie with the given {@code name} |
||||
* whose {@linkplain ResponseCookie#isSecure() secure flag} is equal to the give one. |
||||
* @param name the name of the cookie |
||||
* @param expected whether the cookie is secure |
||||
*/ |
||||
public ResponseCookieMapAssert isSecure(String name, boolean expected) { |
||||
return hasCookieSatisfying(name, cookie -> Assertions.assertThat(cookie.isSecure()).isEqualTo(expected)); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual cookies contain a cookie with the given {@code name} |
||||
* whose {@linkplain ResponseCookie#isHttpOnly() http only flag} is equal to the given |
||||
* one. |
||||
* @param name the name of the cookie |
||||
* @param expected whether the cookie is http only |
||||
*/ |
||||
public ResponseCookieMapAssert isHttpOnly(String name, boolean expected) { |
||||
return hasCookieSatisfying(name, cookie -> Assertions.assertThat(cookie.isHttpOnly()).isEqualTo(expected)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
/* |
||||
* Copyright 2002-present 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 |
||||
* |
||||
* https://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.assertj; |
||||
|
||||
import org.assertj.core.api.AssertProvider; |
||||
|
||||
import org.springframework.test.web.reactive.server.ExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
|
||||
|
||||
/** |
||||
* {@link AssertProvider} for {@link WebTestClientResponseAssert} that holds the |
||||
* result of an exchange performed through {@link WebTestClient}. Intended for |
||||
* further use with AssertJ. For example: |
||||
* |
||||
* <pre class="code"> |
||||
* ResponseSpec spec = webTestClient.get().uri("/greeting").exchange(); |
||||
* |
||||
* WebTestClientResponse response = WebTestClientResponse.from(spec); |
||||
* assertThat(response).hasStatusOk(); |
||||
* assertThat(response).contentType().isCompatibleWith(MediaType.APPLICATION_JSON); |
||||
* assertThat(response).bodyJson().extractingPath("$.message").asString().isEqualTo("Hello World"); |
||||
* </pre> |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 7.0 |
||||
*/ |
||||
public interface WebTestClientResponse extends AssertProvider<WebTestClientResponseAssert> { |
||||
|
||||
/** |
||||
* Return the underlying {@link ExchangeResult}. |
||||
*/ |
||||
ExchangeResult getExchangeResult(); |
||||
|
||||
|
||||
/** |
||||
* Create an instance from a {@link WebTestClient.ResponseSpec}. |
||||
*/ |
||||
static WebTestClientResponse from(WebTestClient.ResponseSpec spec) { |
||||
return from(spec.returnResult(byte[].class)); |
||||
} |
||||
|
||||
/** |
||||
* Create an instance from an {@link ExchangeResult}. |
||||
*/ |
||||
static WebTestClientResponse from(ExchangeResult result) { |
||||
return new DefaultWebTestClientResponse(result); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,337 @@
@@ -0,0 +1,337 @@
|
||||
/* |
||||
* Copyright 2002-present 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 |
||||
* |
||||
* https://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.assertj; |
||||
|
||||
import java.nio.charset.Charset; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Collection; |
||||
import java.util.function.Supplier; |
||||
|
||||
import org.assertj.core.api.AbstractByteArrayAssert; |
||||
import org.assertj.core.api.AbstractIntegerAssert; |
||||
import org.assertj.core.api.AbstractObjectAssert; |
||||
import org.assertj.core.api.AbstractStringAssert; |
||||
import org.assertj.core.api.Assertions; |
||||
import org.assertj.core.api.ByteArrayAssert; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.HttpStatusCode; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.ResponseCookie; |
||||
import org.springframework.test.http.HttpHeadersAssert; |
||||
import org.springframework.test.http.MediaTypeAssert; |
||||
import org.springframework.test.json.AbstractJsonContentAssert; |
||||
import org.springframework.test.json.JsonContent; |
||||
import org.springframework.test.json.JsonContentAssert; |
||||
import org.springframework.test.web.reactive.server.ExchangeResult; |
||||
import org.springframework.util.function.SingletonSupplier; |
||||
|
||||
/** |
||||
* AssertJ {@linkplain org.assertj.core.api.Assert assertions} for the result |
||||
* from a {@link org.springframework.test.web.reactive.server.WebTestClient} |
||||
* exchange. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 7.0 |
||||
*/ |
||||
@SuppressWarnings({"UnusedReturnValue", "unused"}) |
||||
public class WebTestClientResponseAssert |
||||
extends AbstractObjectAssert<WebTestClientResponseAssert, WebTestClientResponse> { |
||||
|
||||
private final Supplier<MediaTypeAssert> contentTypeAssertSupplier; |
||||
|
||||
private final Supplier<HttpHeadersAssert> headersAssertSupplier; |
||||
|
||||
private final Supplier<AbstractIntegerAssert<?>> statusAssert; |
||||
|
||||
|
||||
WebTestClientResponseAssert(WebTestClientResponse actual) { |
||||
super(actual, WebTestClientResponseAssert.class); |
||||
|
||||
this.contentTypeAssertSupplier = SingletonSupplier.of(() -> |
||||
new MediaTypeAssert(getExchangeResult().getResponseHeaders().getContentType())); |
||||
|
||||
this.headersAssertSupplier = SingletonSupplier.of(() -> |
||||
new HttpHeadersAssert(getExchangeResult().getResponseHeaders())); |
||||
|
||||
this.statusAssert = SingletonSupplier.of(() -> |
||||
Assertions.assertThat(getExchangeResult().getStatus().value()).as("HTTP status code")); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Verify that the HTTP status is equal to the specified status code. |
||||
* @param status the expected HTTP status code |
||||
*/ |
||||
public WebTestClientResponseAssert hasStatus(int status) { |
||||
status().isEqualTo(status); |
||||
return this.myself; |
||||
} |
||||
|
||||
/** |
||||
* Verify that the HTTP status is equal to the specified |
||||
* {@linkplain HttpStatus status}. |
||||
* @param status the expected HTTP status code |
||||
*/ |
||||
public WebTestClientResponseAssert hasStatus(HttpStatus status) { |
||||
return hasStatus(status.value()); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the HTTP status is equal to {@link HttpStatus#OK}. |
||||
* @see #hasStatus(HttpStatus) |
||||
*/ |
||||
public WebTestClientResponseAssert hasStatusOk() { |
||||
return hasStatus(HttpStatus.OK); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the HTTP status code is in the 1xx range. |
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.1">RFC 2616</a> |
||||
*/ |
||||
public WebTestClientResponseAssert hasStatus1xxInformational() { |
||||
return hasStatusSeries(HttpStatus.Series.INFORMATIONAL); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the HTTP status code is in the 2xx range. |
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.2">RFC 2616</a> |
||||
*/ |
||||
public WebTestClientResponseAssert hasStatus2xxSuccessful() { |
||||
return hasStatusSeries(HttpStatus.Series.SUCCESSFUL); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the HTTP status code is in the 3xx range. |
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.3">RFC 2616</a> |
||||
*/ |
||||
public WebTestClientResponseAssert hasStatus3xxRedirection() { |
||||
return hasStatusSeries(HttpStatus.Series.REDIRECTION); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the HTTP status code is in the 4xx range. |
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.4">RFC 2616</a> |
||||
*/ |
||||
public WebTestClientResponseAssert hasStatus4xxClientError() { |
||||
return hasStatusSeries(HttpStatus.Series.CLIENT_ERROR); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the HTTP status code is in the 5xx range. |
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.5">RFC 2616</a> |
||||
*/ |
||||
public WebTestClientResponseAssert hasStatus5xxServerError() { |
||||
return hasStatusSeries(HttpStatus.Series.SERVER_ERROR); |
||||
} |
||||
|
||||
private WebTestClientResponseAssert hasStatusSeries(HttpStatus.Series series) { |
||||
HttpStatusCode status = getExchangeResult().getStatus(); |
||||
Assertions.assertThat(HttpStatus.Series.resolve(status.value())).as("HTTP status series").isEqualTo(series); |
||||
return this.myself; |
||||
} |
||||
|
||||
private AbstractIntegerAssert<?> status() { |
||||
return this.statusAssert.get(); |
||||
} |
||||
|
||||
/** |
||||
* Return a new {@linkplain HttpHeadersAssert assertion} object that uses |
||||
* {@link HttpHeaders} as the object to test. The returned assertion object |
||||
* provides all the regular {@linkplain org.assertj.core.api.AbstractMapAssert |
||||
* map assertions}, with headers mapped by header name. |
||||
* Examples: <pre><code class="java"> |
||||
* // Check for the presence of the Accept header:
|
||||
* assertThat(response).headers().containsHeader(HttpHeaders.ACCEPT); |
||||
* |
||||
* // Check for the absence of the Content-Length header:
|
||||
* assertThat(response).headers().doesNotContainsHeader(HttpHeaders.CONTENT_LENGTH); |
||||
* </code></pre> |
||||
*/ |
||||
public HttpHeadersAssert headers() { |
||||
return this.headersAssertSupplier.get(); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the response contains a header with the given {@code name}. |
||||
* @param name the name of an expected HTTP header |
||||
*/ |
||||
public WebTestClientResponseAssert containsHeader(String name) { |
||||
headers().containsHeader(name); |
||||
return this.myself; |
||||
} |
||||
|
||||
/** |
||||
* Verify that the response does not contain a header with the given {@code name}. |
||||
* @param name the name of an HTTP header that should not be present |
||||
*/ |
||||
public WebTestClientResponseAssert doesNotContainHeader(String name) { |
||||
headers().doesNotContainHeader(name); |
||||
return this.myself; |
||||
} |
||||
|
||||
/** |
||||
* Verify that the response contains a header with the given {@code name} |
||||
* and primary {@code value}. |
||||
* @param name the name of an expected HTTP header |
||||
* @param value the expected value of the header |
||||
*/ |
||||
public WebTestClientResponseAssert hasHeader(String name, String value) { |
||||
headers().hasValue(name, value); |
||||
return this.myself; |
||||
} |
||||
|
||||
/** |
||||
* Return a new {@linkplain MediaTypeAssert assertion} object that uses the |
||||
* response's {@linkplain MediaType content type} as the object to test. |
||||
*/ |
||||
public MediaTypeAssert contentType() { |
||||
return this.contentTypeAssertSupplier.get(); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the response's {@code Content-Type} is equal to the given value. |
||||
* @param contentType the expected content type |
||||
*/ |
||||
public WebTestClientResponseAssert hasContentType(MediaType contentType) { |
||||
contentType().isEqualTo(contentType); |
||||
return this.myself; |
||||
} |
||||
|
||||
/** |
||||
* Verify that the response's {@code Content-Type} is equal to the given |
||||
* string representation. |
||||
* @param contentType the expected content type |
||||
*/ |
||||
public WebTestClientResponseAssert hasContentType(String contentType) { |
||||
contentType().isEqualTo(contentType); |
||||
return this.myself; |
||||
} |
||||
|
||||
/** |
||||
* Verify that the response's {@code Content-Type} is |
||||
* {@linkplain MediaType#isCompatibleWith(MediaType) compatible} with the |
||||
* given value. |
||||
* @param contentType the expected compatible content type |
||||
*/ |
||||
public WebTestClientResponseAssert hasContentTypeCompatibleWith(MediaType contentType) { |
||||
contentType().isCompatibleWith(contentType); |
||||
return this.myself; |
||||
} |
||||
|
||||
/** |
||||
* Verify that the response's {@code Content-Type} is |
||||
* {@linkplain MediaType#isCompatibleWith(MediaType) compatible} with the |
||||
* given string representation. |
||||
* @param contentType the expected compatible content type |
||||
*/ |
||||
public WebTestClientResponseAssert hasContentTypeCompatibleWith(String contentType) { |
||||
contentType().isCompatibleWith(contentType); |
||||
return this.myself; |
||||
} |
||||
|
||||
/** |
||||
* Return a new {@linkplain ResponseCookieMapAssert assertion} object that uses the |
||||
* response's {@linkplain ResponseCookie cookies} as the object to test. |
||||
*/ |
||||
public ResponseCookieMapAssert cookies() { |
||||
return new ResponseCookieMapAssert(getCookies()); |
||||
} |
||||
|
||||
private ResponseCookie[] getCookies() { |
||||
return getExchangeResult().getResponseCookies().values().stream() |
||||
.flatMap(Collection::stream) |
||||
.toArray(ResponseCookie[]:: new); |
||||
} |
||||
|
||||
/** |
||||
* Return a new {@linkplain AbstractByteArrayAssert assertion} object that |
||||
* uses the response body as the object to test. |
||||
* @see #bodyText() |
||||
* @see #bodyJson() |
||||
*/ |
||||
public AbstractByteArrayAssert<?> body() { |
||||
return new ByteArrayAssert(getExchangeResult().getResponseBodyContent()); |
||||
} |
||||
|
||||
/** |
||||
* Return a new {@linkplain AbstractStringAssert assertion} object that uses |
||||
* the response body converted to text as the object to test. |
||||
* <p>Examples: <pre><code class="java"> |
||||
* // Check that the response body is equal to "Hello World":
|
||||
* assertThat(response).bodyText().isEqualTo("Hello World"); |
||||
* </code></pre> |
||||
*/ |
||||
public AbstractStringAssert<?> bodyText() { |
||||
return Assertions.assertThat(readBody()); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the response body is equal to the given value. |
||||
*/ |
||||
public WebTestClientResponseAssert hasBodyTextEqualTo(String bodyText) { |
||||
bodyText().isEqualTo(bodyText); |
||||
return this.myself; |
||||
} |
||||
|
||||
/** |
||||
* Return a new {@linkplain AbstractJsonContentAssert assertion} object that |
||||
* uses the response body converted to text as the object to test. Compared |
||||
* to {@link #bodyText()}, the assertion object provides dedicated JSON |
||||
* support. |
||||
* <p>Examples: <pre><code class="java"> |
||||
* // Check that the response body is strictly equal to the content of
|
||||
* // "/com/acme/sample/person-created.json":
|
||||
* assertThat(response).bodyJson() |
||||
* .isStrictlyEqualToJson("/com/acme/sample/person-created.json"); |
||||
* |
||||
* // Check that the response is strictly equal to the content of the
|
||||
* // specified file located in the same package as the PersonController:
|
||||
* assertThat(response).bodyJson().withResourceLoadClass(PersonController.class) |
||||
* .isStrictlyEqualToJson("person-created.json"); |
||||
* </code></pre> |
||||
* The returned assert object also supports JSON path expressions. |
||||
* <p>Examples: <pre><code class="java"> |
||||
* // Check that the JSON document does not have an "error" element
|
||||
* assertThat(response).bodyJson().doesNotHavePath("$.error"); |
||||
* |
||||
* // Check that the JSON document as a top level "message" element
|
||||
* assertThat(response).bodyJson() |
||||
* .extractingPath("$.message").asString().isEqualTo("hello"); |
||||
* </code></pre> |
||||
*/ |
||||
public AbstractJsonContentAssert<?> bodyJson() { |
||||
return new JsonContentAssert(new JsonContent(readBody(), getExchangeResult().getJsonConverterDelegate())); |
||||
} |
||||
|
||||
private String readBody() { |
||||
return new String(getExchangeResult().getResponseBodyContent(), getCharset()); |
||||
} |
||||
|
||||
private Charset getCharset() { |
||||
ExchangeResult result = getExchangeResult(); |
||||
MediaType contentType = result.getResponseHeaders().getContentType(); |
||||
Charset charset = (contentType != null ? contentType.getCharset() : null); |
||||
return (charset != null ? charset : StandardCharsets.UTF_8); |
||||
} |
||||
|
||||
private ExchangeResult getExchangeResult() { |
||||
return this.actual.getExchangeResult(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
/** |
||||
* AssertJ support for WebTestClient. |
||||
*/ |
||||
@NullMarked |
||||
package org.springframework.test.web.reactive.server.assertj; |
||||
|
||||
import org.jspecify.annotations.NullMarked; |
||||
@ -0,0 +1,176 @@
@@ -0,0 +1,176 @@
|
||||
/* |
||||
* Copyright 2002-present 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 |
||||
* |
||||
* https://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.assertj; |
||||
|
||||
import java.time.Duration; |
||||
import java.util.List; |
||||
|
||||
import org.assertj.core.api.AssertProvider; |
||||
import org.junit.jupiter.api.BeforeAll; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.ResponseCookie; |
||||
import org.springframework.test.web.servlet.assertj.CookieMapAssert; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* Tests for {@link CookieMapAssert}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
class ResponseCookieMapAssertTests { |
||||
|
||||
static ResponseCookie[] cookies; |
||||
|
||||
@BeforeAll |
||||
static void setup() { |
||||
ResponseCookie framework = ResponseCookie.from("framework", "spring").secure(true).httpOnly(true).build(); |
||||
ResponseCookie age = ResponseCookie.from("age", "value").maxAge(1200).build(); |
||||
ResponseCookie domain = ResponseCookie.from("domain", "value").domain("spring.io").build(); |
||||
ResponseCookie path = ResponseCookie.from("path", "value").path("/spring").build(); |
||||
cookies = List.of(framework, age, domain, path).toArray(new ResponseCookie[0]); |
||||
} |
||||
|
||||
@Test |
||||
void containsCookieWhenCookieExistsShouldPass() { |
||||
assertThat(cookies()).containsCookie("framework"); |
||||
} |
||||
|
||||
@Test |
||||
void containsCookieWhenCookieMissingShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).containsCookie("missing")); |
||||
} |
||||
|
||||
@Test |
||||
void containsCookiesWhenCookiesExistShouldPass() { |
||||
assertThat(cookies()).containsCookies("framework", "age"); |
||||
} |
||||
|
||||
@Test |
||||
void containsCookiesWhenCookieMissingShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).containsCookies("framework", "missing")); |
||||
} |
||||
|
||||
@Test |
||||
void doesNotContainCookieWhenCookieMissingShouldPass() { |
||||
assertThat(cookies()).doesNotContainCookie("missing"); |
||||
} |
||||
|
||||
@Test |
||||
void doesNotContainCookieWhenCookieExistsShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).doesNotContainCookie("framework")); |
||||
} |
||||
|
||||
@Test |
||||
void doesNotContainCookiesWhenCookiesMissingShouldPass() { |
||||
assertThat(cookies()).doesNotContainCookies("missing", "missing2"); |
||||
} |
||||
|
||||
@Test |
||||
void doesNotContainCookiesWhenAtLeastOneCookieExistShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).doesNotContainCookies("missing", "framework")); |
||||
} |
||||
|
||||
@Test |
||||
void hasValueEqualsWhenCookieValueMatchesShouldPass() { |
||||
assertThat(cookies()).hasValue("framework", "spring"); |
||||
} |
||||
|
||||
@Test |
||||
void hasValueEqualsWhenCookieValueDiffersShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).hasValue("framework", "other")); |
||||
} |
||||
|
||||
@Test |
||||
void hasCookieSatisfyingWhenCookieValueMatchesShouldPass() { |
||||
assertThat(cookies()).hasCookieSatisfying("framework", cookie -> assertThat(cookie.getValue()).startsWith("spr")); |
||||
} |
||||
|
||||
@Test |
||||
void hasCookieSatisfyingWhenCookieValueDiffersShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).hasCookieSatisfying( |
||||
"framework", cookie -> assertThat(cookie.getValue()).startsWith("not"))); |
||||
} |
||||
|
||||
@Test |
||||
void hasMaxAgeWhenCookieAgeMatchesShouldPass() { |
||||
assertThat(cookies()).hasMaxAge("age", Duration.ofMinutes(20)); |
||||
} |
||||
|
||||
@Test |
||||
void hasMaxAgeWhenCookieAgeDiffersShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).hasMaxAge("age", Duration.ofMinutes(30))); |
||||
} |
||||
|
||||
@Test |
||||
void pathWhenCookiePathMatchesShouldPass() { |
||||
assertThat(cookies()).hasPath("path", "/spring"); |
||||
} |
||||
|
||||
@Test |
||||
void pathWhenCookiePathDiffersShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).hasPath("path", "/other")); |
||||
} |
||||
|
||||
@Test |
||||
void hasDomainWhenCookieDomainMatchesShouldPass() { |
||||
assertThat(cookies()).hasDomain("domain", "spring.io"); |
||||
} |
||||
|
||||
@Test |
||||
void hasDomainWhenCookieDomainDiffersShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).hasDomain("domain", "example.org")); |
||||
} |
||||
|
||||
@Test |
||||
void isSecureWhenCookieSecureMatchesShouldPass() { |
||||
assertThat(cookies()).isSecure("framework", true); |
||||
} |
||||
|
||||
@Test |
||||
void isSecureWhenCookieSecureDiffersShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).isSecure("domain", true)); |
||||
} |
||||
|
||||
@Test |
||||
void isHttpOnlyWhenCookieHttpOnlyMatchesShouldPass() { |
||||
assertThat(cookies()).isHttpOnly("framework", true); |
||||
} |
||||
|
||||
@Test |
||||
void isHttpOnlyWhenCookieHttpOnlyDiffersShouldFail() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(cookies()).isHttpOnly("domain", true)); |
||||
} |
||||
|
||||
private static AssertProvider<ResponseCookieMapAssert> cookies() { |
||||
return () -> new ResponseCookieMapAssert(cookies); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,126 @@
@@ -0,0 +1,126 @@
|
||||
/* |
||||
* Copyright 2002-present 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 |
||||
* |
||||
* https://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.assertj; |
||||
|
||||
|
||||
import java.util.Map; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.ResponseCookie; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.server.ServerWebExchange; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Unit tests for {@link WebTestClientResponse}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class WebTestClientResponseTests { |
||||
|
||||
private final WebTestClient client = WebTestClient.bindToController(HelloController.class).build(); |
||||
|
||||
|
||||
@Test |
||||
void status() { |
||||
ResponseSpec spec = client.get().uri("/greeting").exchange(); |
||||
assertThat(WebTestClientResponse.from(spec)).hasStatusOk().hasStatus2xxSuccessful(); |
||||
} |
||||
|
||||
@Test |
||||
void headers() { |
||||
ResponseSpec spec = client.get().uri("/greeting").exchange(); |
||||
|
||||
WebTestClientResponse response = WebTestClientResponse.from(spec); |
||||
assertThat(response).hasStatusOk(); |
||||
assertThat(response).headers() |
||||
.containsOnlyHeaders(HttpHeaders.CONTENT_TYPE, HttpHeaders.CONTENT_LENGTH) |
||||
.hasValue(HttpHeaders.CONTENT_TYPE, "text/plain;charset=UTF-8") |
||||
.hasValue(HttpHeaders.CONTENT_LENGTH, 11); |
||||
} |
||||
|
||||
@Test |
||||
void contentType() { |
||||
ResponseSpec spec = client.get().uri("/greeting").exchange(); |
||||
|
||||
WebTestClientResponse response = WebTestClientResponse.from(spec); |
||||
assertThat(response).hasStatusOk(); |
||||
assertThat(response).contentType().isEqualTo("text/plain;charset=UTF-8"); |
||||
assertThat(response).hasContentTypeCompatibleWith(MediaType.TEXT_PLAIN); |
||||
} |
||||
|
||||
@Test |
||||
void cookies() { |
||||
ResponseSpec spec = client.get().uri("/cookie").exchange(); |
||||
|
||||
WebTestClientResponse response = WebTestClientResponse.from(spec); |
||||
assertThat(response).hasStatusOk(); |
||||
assertThat(response).cookies().hasValue("foo", "bar"); |
||||
assertThat(response).body().isEmpty(); |
||||
} |
||||
|
||||
@Test |
||||
void bodyText() { |
||||
ResponseSpec spec = client.get().uri("/greeting").exchange(); |
||||
|
||||
WebTestClientResponse response = WebTestClientResponse.from(spec); |
||||
assertThat(response).hasStatusOk(); |
||||
assertThat(response).contentType().isCompatibleWith(MediaType.TEXT_PLAIN); |
||||
assertThat(response).bodyText().isEqualTo("Hello World"); |
||||
assertThat(response).hasBodyTextEqualTo("Hello World"); |
||||
} |
||||
|
||||
@Test |
||||
void bodyJson() { |
||||
ResponseSpec spec = client.get().uri("/message").exchange(); |
||||
|
||||
WebTestClientResponse response = WebTestClientResponse.from(spec); |
||||
assertThat(response).hasStatusOk(); |
||||
assertThat(response).contentType().isEqualTo(MediaType.APPLICATION_JSON); |
||||
assertThat(response).bodyJson().extractingPath("$.message").asString().isEqualTo("Hello World"); |
||||
} |
||||
|
||||
|
||||
@SuppressWarnings("unused") |
||||
@RestController |
||||
private static class HelloController { |
||||
|
||||
@GetMapping("/greeting") |
||||
public String getGreeting() { |
||||
return "Hello World"; |
||||
} |
||||
|
||||
@GetMapping("/message") |
||||
public Map<String, ?> getMessage() { |
||||
return Map.of("message", "Hello World"); |
||||
} |
||||
|
||||
@GetMapping("/cookie") |
||||
public void getCookie(ServerWebExchange exchange) { |
||||
ResponseCookie cookie = ResponseCookie.from("foo", "bar").build(); |
||||
exchange.getResponse().addCookie(cookie); |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue