Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.springframework.test.web.reactive.server.DefaultWebTestClient Maven / Gradle / Ivy
/*
* 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
*
* 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;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Function;
import org.hamcrest.Matcher;
import org.hamcrest.MatcherAssert;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.test.util.AssertionErrors;
import org.springframework.test.util.JsonExpectationsHelper;
import org.springframework.test.util.XmlExpectationsHelper;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriBuilder;
/**
* Default implementation of {@link WebTestClient}.
*
* @author Rossen Stoyanchev
* @since 5.0
*/
class DefaultWebTestClient implements WebTestClient {
private final WebClient webClient;
private final WiretapConnector wiretapConnector;
private final Duration timeout;
private final DefaultWebTestClientBuilder builder;
private final AtomicLong requestIndex = new AtomicLong();
DefaultWebTestClient(WebClient.Builder clientBuilder, ClientHttpConnector connector,
@Nullable Duration timeout, DefaultWebTestClientBuilder webTestClientBuilder) {
Assert.notNull(clientBuilder, "WebClient.Builder is required");
this.wiretapConnector = new WiretapConnector(connector);
this.webClient = clientBuilder.clientConnector(this.wiretapConnector).build();
this.timeout = (timeout != null ? timeout : Duration.ofSeconds(5));
this.builder = webTestClientBuilder;
}
private Duration getTimeout() {
return this.timeout;
}
@Override
public RequestHeadersUriSpec get() {
return methodInternal(HttpMethod.GET);
}
@Override
public RequestHeadersUriSpec head() {
return methodInternal(HttpMethod.HEAD);
}
@Override
public RequestBodyUriSpec post() {
return methodInternal(HttpMethod.POST);
}
@Override
public RequestBodyUriSpec put() {
return methodInternal(HttpMethod.PUT);
}
@Override
public RequestBodyUriSpec patch() {
return methodInternal(HttpMethod.PATCH);
}
@Override
public RequestHeadersUriSpec delete() {
return methodInternal(HttpMethod.DELETE);
}
@Override
public RequestHeadersUriSpec options() {
return methodInternal(HttpMethod.OPTIONS);
}
@Override
public RequestBodyUriSpec method(HttpMethod method) {
return methodInternal(method);
}
private RequestBodyUriSpec methodInternal(HttpMethod method) {
return new DefaultRequestBodyUriSpec(this.webClient.method(method));
}
@Override
public Builder mutate() {
return new DefaultWebTestClientBuilder(this.builder);
}
@Override
public WebTestClient mutateWith(WebTestClientConfigurer configurer) {
return mutate().apply(configurer).build();
}
private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
private final WebClient.RequestBodyUriSpec bodySpec;
@Nullable
private String uriTemplate;
private final String requestId;
DefaultRequestBodyUriSpec(WebClient.RequestBodyUriSpec spec) {
this.bodySpec = spec;
this.requestId = String.valueOf(requestIndex.incrementAndGet());
this.bodySpec.header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, this.requestId);
}
@Override
public RequestBodySpec uri(String uriTemplate, Object... uriVariables) {
this.bodySpec.uri(uriTemplate, uriVariables);
this.uriTemplate = uriTemplate;
return this;
}
@Override
public RequestBodySpec uri(String uriTemplate, Map uriVariables) {
this.bodySpec.uri(uriTemplate, uriVariables);
this.uriTemplate = uriTemplate;
return this;
}
@Override
public RequestBodySpec uri(Function uriFunction) {
this.bodySpec.uri(uriFunction);
this.uriTemplate = null;
return this;
}
@Override
public RequestBodySpec uri(URI uri) {
this.bodySpec.uri(uri);
this.uriTemplate = null;
return this;
}
@Override
public RequestBodySpec header(String headerName, String... headerValues) {
this.bodySpec.header(headerName, headerValues);
return this;
}
@Override
public RequestBodySpec headers(Consumer headersConsumer) {
this.bodySpec.headers(headersConsumer);
return this;
}
@Override
public RequestBodySpec attribute(String name, Object value) {
this.bodySpec.attribute(name, value);
return this;
}
@Override
public RequestBodySpec attributes(
Consumer> attributesConsumer) {
this.bodySpec.attributes(attributesConsumer);
return this;
}
@Override
public RequestBodySpec accept(MediaType... acceptableMediaTypes) {
this.bodySpec.accept(acceptableMediaTypes);
return this;
}
@Override
public RequestBodySpec acceptCharset(Charset... acceptableCharsets) {
this.bodySpec.acceptCharset(acceptableCharsets);
return this;
}
@Override
public RequestBodySpec contentType(MediaType contentType) {
this.bodySpec.contentType(contentType);
return this;
}
@Override
public RequestBodySpec contentLength(long contentLength) {
this.bodySpec.contentLength(contentLength);
return this;
}
@Override
public RequestBodySpec cookie(String name, String value) {
this.bodySpec.cookie(name, value);
return this;
}
@Override
public RequestBodySpec cookies(
Consumer> cookiesConsumer) {
this.bodySpec.cookies(cookiesConsumer);
return this;
}
@Override
public RequestBodySpec ifModifiedSince(ZonedDateTime ifModifiedSince) {
this.bodySpec.ifModifiedSince(ifModifiedSince);
return this;
}
@Override
public RequestBodySpec ifNoneMatch(String... ifNoneMatches) {
this.bodySpec.ifNoneMatch(ifNoneMatches);
return this;
}
@Override
public RequestHeadersSpec body(BodyInserter inserter) {
this.bodySpec.body(inserter);
return this;
}
@Override
public > RequestHeadersSpec body(S publisher, Class elementClass) {
this.bodySpec.body(publisher, elementClass);
return this;
}
@Override
public RequestHeadersSpec syncBody(Object body) {
this.bodySpec.syncBody(body);
return this;
}
@Override
public ResponseSpec exchange() {
ClientResponse clientResponse = this.bodySpec.exchange().block(getTimeout());
Assert.state(clientResponse != null, "No ClientResponse");
WiretapConnector.Info info = wiretapConnector.claimRequest(this.requestId);
return new DefaultResponseSpec(info, clientResponse, this.uriTemplate, getTimeout());
}
}
private static class DefaultResponseSpec implements ResponseSpec {
private final ExchangeResult exchangeResult;
private final ClientResponse response;
private final Duration timeout;
DefaultResponseSpec(WiretapConnector.Info wiretapInfo, ClientResponse response,
@Nullable String uriTemplate, Duration timeout) {
this.exchangeResult = wiretapInfo.createExchangeResult(timeout, uriTemplate);
this.response = response;
this.timeout = timeout;
}
@Override
public StatusAssertions expectStatus() {
return new StatusAssertions(this.exchangeResult, this);
}
@Override
public HeaderAssertions expectHeader() {
return new HeaderAssertions(this.exchangeResult, this);
}
@Override
public BodySpec expectBody(Class bodyType) {
B body = this.response.bodyToMono(bodyType).block(this.timeout);
EntityExchangeResult entityResult = new EntityExchangeResult<>(this.exchangeResult, body);
return new DefaultBodySpec<>(entityResult);
}
@Override
public BodySpec expectBody(ParameterizedTypeReference bodyType) {
B body = this.response.bodyToMono(bodyType).block(this.timeout);
EntityExchangeResult entityResult = new EntityExchangeResult<>(this.exchangeResult, body);
return new DefaultBodySpec<>(entityResult);
}
@Override
public ListBodySpec expectBodyList(Class elementType) {
return getListBodySpec(this.response.bodyToFlux(elementType));
}
@Override
public ListBodySpec expectBodyList(ParameterizedTypeReference elementType) {
Flux flux = this.response.bodyToFlux(elementType);
return getListBodySpec(flux);
}
private ListBodySpec getListBodySpec(Flux flux) {
List body = flux.collectList().block(this.timeout);
EntityExchangeResult> entityResult = new EntityExchangeResult<>(this.exchangeResult, body);
return new DefaultListBodySpec<>(entityResult);
}
@Override
public BodyContentSpec expectBody() {
ByteArrayResource resource = this.response.bodyToMono(ByteArrayResource.class).block(this.timeout);
byte[] body = (resource != null ? resource.getByteArray() : null);
EntityExchangeResult entityResult = new EntityExchangeResult<>(this.exchangeResult, body);
return new DefaultBodyContentSpec(entityResult);
}
@Override
public FluxExchangeResult returnResult(Class elementType) {
Flux body = this.response.bodyToFlux(elementType);
return new FluxExchangeResult<>(this.exchangeResult, body);
}
@Override
public FluxExchangeResult returnResult(ParameterizedTypeReference elementType) {
Flux body = this.response.bodyToFlux(elementType);
return new FluxExchangeResult<>(this.exchangeResult, body);
}
}
private static class DefaultBodySpec> implements BodySpec {
private final EntityExchangeResult result;
DefaultBodySpec(EntityExchangeResult result) {
this.result = result;
}
protected EntityExchangeResult getResult() {
return this.result;
}
@Override
public T isEqualTo(B expected) {
this.result.assertWithDiagnostics(() ->
AssertionErrors.assertEquals("Response body", expected, this.result.getResponseBody()));
return self();
}
@Override
public T value(Matcher matcher) {
this.result.assertWithDiagnostics(() -> MatcherAssert.assertThat(this.result.getResponseBody(), matcher));
return self();
}
@Override
public T value(Function bodyMapper, Matcher matcher) {
this.result.assertWithDiagnostics(() -> {
B body = this.result.getResponseBody();
MatcherAssert.assertThat(bodyMapper.apply(body), matcher);
});
return self();
}
@Override
public T value(Consumer consumer) {
this.result.assertWithDiagnostics(() -> consumer.accept(this.result.getResponseBody()));
return self();
}
@Override
public T consumeWith(Consumer> consumer) {
this.result.assertWithDiagnostics(() -> consumer.accept(this.result));
return self();
}
@SuppressWarnings("unchecked")
private T self() {
return (T) this;
}
@Override
public EntityExchangeResult returnResult() {
return this.result;
}
}
private static class DefaultListBodySpec extends DefaultBodySpec, ListBodySpec>
implements ListBodySpec {
DefaultListBodySpec(EntityExchangeResult> result) {
super(result);
}
@Override
public ListBodySpec hasSize(int size) {
List actual = getResult().getResponseBody();
String message = "Response body does not contain " + size + " elements";
getResult().assertWithDiagnostics(() ->
AssertionErrors.assertEquals(message, size, (actual != null ? actual.size() : 0)));
return this;
}
@Override
@SuppressWarnings("unchecked")
public ListBodySpec contains(E... elements) {
List expected = Arrays.asList(elements);
List actual = getResult().getResponseBody();
String message = "Response body does not contain " + expected;
getResult().assertWithDiagnostics(() ->
AssertionErrors.assertTrue(message, (actual != null && actual.containsAll(expected))));
return this;
}
@Override
@SuppressWarnings("unchecked")
public ListBodySpec doesNotContain(E... elements) {
List expected = Arrays.asList(elements);
List actual = getResult().getResponseBody();
String message = "Response body should not have contained " + expected;
getResult().assertWithDiagnostics(() ->
AssertionErrors.assertTrue(message, (actual == null || !actual.containsAll(expected))));
return this;
}
@Override
public EntityExchangeResult> returnResult() {
return getResult();
}
}
private static class DefaultBodyContentSpec implements BodyContentSpec {
private final EntityExchangeResult result;
private final boolean isEmpty;
DefaultBodyContentSpec(EntityExchangeResult result) {
this.result = result;
this.isEmpty = (result.getResponseBody() == null || result.getResponseBody().length == 0);
}
@Override
public EntityExchangeResult isEmpty() {
this.result.assertWithDiagnostics(() ->
AssertionErrors.assertTrue("Expected empty body", this.isEmpty));
return new EntityExchangeResult<>(this.result, null);
}
@Override
public BodyContentSpec json(String json) {
this.result.assertWithDiagnostics(() -> {
try {
new JsonExpectationsHelper().assertJsonEqual(json, getBodyAsString());
}
catch (Exception ex) {
throw new AssertionError("JSON parsing error", ex);
}
});
return this;
}
@Override
public BodyContentSpec xml(String expectedXml) {
this.result.assertWithDiagnostics(() -> {
try {
new XmlExpectationsHelper().assertXmlEqual(expectedXml, getBodyAsString());
}
catch (Exception ex) {
throw new AssertionError("XML parsing error", ex);
}
});
return this;
}
@Override
public JsonPathAssertions jsonPath(String expression, Object... args) {
return new JsonPathAssertions(this, getBodyAsString(), expression, args);
}
@Override
public XpathAssertions xpath(String expression, @Nullable Map namespaces, Object... args) {
return new XpathAssertions(this, expression, namespaces, args);
}
private String getBodyAsString() {
byte[] body = this.result.getResponseBody();
if (body == null || body.length == 0) {
return "";
}
Charset charset = Optional.ofNullable(this.result.getResponseHeaders().getContentType())
.map(MimeType::getCharset).orElse(StandardCharsets.UTF_8);
return new String(body, charset);
}
@Override
public BodyContentSpec consumeWith(Consumer> consumer) {
this.result.assertWithDiagnostics(() -> consumer.accept(this.result));
return this;
}
@Override
public EntityExchangeResult returnResult() {
return this.result;
}
}
}