Support global Consumer<ExchangeResult> in WebTestClient
Closes gh-26662
This commit is contained in:
parent
8bf16ee1f4
commit
c6b271f1b6
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
|
@ -79,6 +79,8 @@ class DefaultWebTestClient implements WebTestClient {
|
|||
@Nullable
|
||||
private final MultiValueMap<String, String> defaultCookies;
|
||||
|
||||
private final Consumer<EntityExchangeResult<?>> entityResultConsumer;
|
||||
|
||||
private final Duration responseTimeout;
|
||||
|
||||
private final DefaultWebTestClientBuilder builder;
|
||||
|
@ -89,6 +91,7 @@ class DefaultWebTestClient implements WebTestClient {
|
|||
DefaultWebTestClient(ClientHttpConnector connector,
|
||||
Function<ClientHttpConnector, ExchangeFunction> exchangeFactory, UriBuilderFactory uriBuilderFactory,
|
||||
@Nullable HttpHeaders headers, @Nullable MultiValueMap<String, String> cookies,
|
||||
Consumer<EntityExchangeResult<?>> entityResultConsumer,
|
||||
@Nullable Duration responseTimeout, DefaultWebTestClientBuilder clientBuilder) {
|
||||
|
||||
this.wiretapConnector = new WiretapConnector(connector);
|
||||
|
@ -96,6 +99,7 @@ class DefaultWebTestClient implements WebTestClient {
|
|||
this.uriBuilderFactory = uriBuilderFactory;
|
||||
this.defaultHeaders = headers;
|
||||
this.defaultCookies = cookies;
|
||||
this.entityResultConsumer = entityResultConsumer;
|
||||
this.responseTimeout = (responseTimeout != null ? responseTimeout : Duration.ofSeconds(5));
|
||||
this.builder = clientBuilder;
|
||||
}
|
||||
|
@ -357,7 +361,8 @@ class DefaultWebTestClient implements WebTestClient {
|
|||
ExchangeResult result = wiretapConnector.getExchangeResult(
|
||||
this.requestId, this.uriTemplate, getResponseTimeout());
|
||||
|
||||
return new DefaultResponseSpec(result, response, getResponseTimeout());
|
||||
return new DefaultResponseSpec(result, response,
|
||||
DefaultWebTestClient.this.entityResultConsumer, getResponseTimeout());
|
||||
}
|
||||
|
||||
private ClientRequest.Builder initRequestBuilder() {
|
||||
|
@ -408,12 +413,19 @@ class DefaultWebTestClient implements WebTestClient {
|
|||
|
||||
private final ClientResponse response;
|
||||
|
||||
private final Consumer<EntityExchangeResult<?>> entityResultConsumer;
|
||||
|
||||
private final Duration timeout;
|
||||
|
||||
|
||||
DefaultResponseSpec(ExchangeResult exchangeResult, ClientResponse response, Duration timeout) {
|
||||
DefaultResponseSpec(
|
||||
ExchangeResult exchangeResult, ClientResponse response,
|
||||
Consumer<EntityExchangeResult<?>> entityResultConsumer,
|
||||
Duration timeout) {
|
||||
|
||||
this.exchangeResult = exchangeResult;
|
||||
this.response = response;
|
||||
this.entityResultConsumer = entityResultConsumer;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
|
@ -435,14 +447,14 @@ class DefaultWebTestClient implements WebTestClient {
|
|||
@Override
|
||||
public <B> BodySpec<B, ?> expectBody(Class<B> bodyType) {
|
||||
B body = this.response.bodyToMono(bodyType).block(this.timeout);
|
||||
EntityExchangeResult<B> entityResult = new EntityExchangeResult<>(this.exchangeResult, body);
|
||||
EntityExchangeResult<B> entityResult = initEntityExchangeResult(body);
|
||||
return new DefaultBodySpec<>(entityResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B> BodySpec<B, ?> expectBody(ParameterizedTypeReference<B> bodyType) {
|
||||
B body = this.response.bodyToMono(bodyType).block(this.timeout);
|
||||
EntityExchangeResult<B> entityResult = new EntityExchangeResult<>(this.exchangeResult, body);
|
||||
EntityExchangeResult<B> entityResult = initEntityExchangeResult(body);
|
||||
return new DefaultBodySpec<>(entityResult);
|
||||
}
|
||||
|
||||
|
@ -459,7 +471,7 @@ class DefaultWebTestClient implements WebTestClient {
|
|||
|
||||
private <E> ListBodySpec<E> getListBodySpec(Flux<E> flux) {
|
||||
List<E> body = flux.collectList().block(this.timeout);
|
||||
EntityExchangeResult<List<E>> entityResult = new EntityExchangeResult<>(this.exchangeResult, body);
|
||||
EntityExchangeResult<List<E>> entityResult = initEntityExchangeResult(body);
|
||||
return new DefaultListBodySpec<>(entityResult);
|
||||
}
|
||||
|
||||
|
@ -467,10 +479,16 @@ class DefaultWebTestClient implements WebTestClient {
|
|||
public BodyContentSpec expectBody() {
|
||||
ByteArrayResource resource = this.response.bodyToMono(ByteArrayResource.class).block(this.timeout);
|
||||
byte[] body = (resource != null ? resource.getByteArray() : null);
|
||||
EntityExchangeResult<byte[]> entityResult = new EntityExchangeResult<>(this.exchangeResult, body);
|
||||
EntityExchangeResult<byte[]> entityResult = initEntityExchangeResult(body);
|
||||
return new DefaultBodyContentSpec(entityResult);
|
||||
}
|
||||
|
||||
private <B> EntityExchangeResult<B> initEntityExchangeResult(@Nullable B body) {
|
||||
EntityExchangeResult<B> result = new EntityExchangeResult<>(this.exchangeResult, body);
|
||||
result.assertWithDiagnostics(() -> this.entityResultConsumer.accept(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> FluxExchangeResult<T> returnResult(Class<T> elementClass) {
|
||||
Flux<T> body;
|
||||
|
|
|
@ -92,6 +92,8 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
|
|||
@Nullable
|
||||
private List<ExchangeFilterFunction> filters;
|
||||
|
||||
private Consumer<EntityExchangeResult<?>> entityResultConsumer = result -> {};
|
||||
|
||||
@Nullable
|
||||
private ExchangeStrategies strategies;
|
||||
|
||||
|
@ -149,6 +151,7 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
|
|||
this.defaultCookies = (other.defaultCookies != null ?
|
||||
new LinkedMultiValueMap<>(other.defaultCookies) : null);
|
||||
this.filters = (other.filters != null ? new ArrayList<>(other.filters) : null);
|
||||
this.entityResultConsumer = other.entityResultConsumer;
|
||||
this.strategies = other.strategies;
|
||||
this.strategiesConfigurers = (other.strategiesConfigurers != null ?
|
||||
new ArrayList<>(other.strategiesConfigurers) : null);
|
||||
|
@ -207,7 +210,7 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
|
|||
|
||||
@Override
|
||||
public WebTestClient.Builder filter(ExchangeFilterFunction filter) {
|
||||
Assert.notNull(filter, "ExchangeFilterFunction must not be null");
|
||||
Assert.notNull(filter, "ExchangeFilterFunction is required");
|
||||
initFilters().add(filter);
|
||||
return this;
|
||||
}
|
||||
|
@ -225,6 +228,13 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
|
|||
return this.filters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder entityExchangeResultConsumer(Consumer<EntityExchangeResult<?>> entityResultConsumer) {
|
||||
Assert.notNull(entityResultConsumer, "`entityResultConsumer` is required");
|
||||
this.entityResultConsumer = this.entityResultConsumer.andThen(entityResultConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder codecs(Consumer<ClientCodecConfigurer> configurer) {
|
||||
if (this.strategiesConfigurers == null) {
|
||||
|
@ -287,7 +297,7 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
|
|||
return new DefaultWebTestClient(connectorToUse, exchangeFactory, initUriBuilderFactory(),
|
||||
this.defaultHeaders != null ? HttpHeaders.readOnlyHttpHeaders(this.defaultHeaders) : null,
|
||||
this.defaultCookies != null ? CollectionUtils.unmodifiableMultiValueMap(this.defaultCookies) : null,
|
||||
this.responseTimeout, new DefaultWebTestClientBuilder(this));
|
||||
this.entityResultConsumer, this.responseTimeout, new DefaultWebTestClientBuilder(this));
|
||||
}
|
||||
|
||||
private static ClientHttpConnector initConnector() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
|
@ -438,6 +438,33 @@ public interface WebTestClient {
|
|||
*/
|
||||
Builder filters(Consumer<List<ExchangeFilterFunction>> filtersConsumer);
|
||||
|
||||
/**
|
||||
* Configure an {@code EntityExchangeResult} callback that is invoked
|
||||
* every time after a response is fully decoded to a single entity, to a
|
||||
* List of entities, or to a byte[]. In effect, equivalent to each and
|
||||
* all of the below but registered once, globally:
|
||||
* <pre>
|
||||
* client.get().uri("/accounts/1")
|
||||
* .exchange()
|
||||
* .expectBody(Person.class).consumeWith(exchangeResult -> ... ));
|
||||
*
|
||||
* client.get().uri("/accounts")
|
||||
* .exchange()
|
||||
* .expectBodyList(Person.class).consumeWith(exchangeResult -> ... ));
|
||||
*
|
||||
* client.get().uri("/accounts/1")
|
||||
* .exchange()
|
||||
* .expectBody().consumeWith(exchangeResult -> ... ));
|
||||
* </pre>
|
||||
* <p>Note that the configured consumer does not apply to responses
|
||||
* decoded to {@code Flux<T>} which can be consumed outside the workflow
|
||||
* of the test client, for example via {@code reactor.test.StepVerifier}.
|
||||
* @param consumer the consumer to apply to entity responses
|
||||
* @return the builder
|
||||
* @since 5.3.5
|
||||
*/
|
||||
Builder entityExchangeResultConsumer(Consumer<EntityExchangeResult<?>> consumer);
|
||||
|
||||
/**
|
||||
* Configure the codecs for the {@code WebClient} in the
|
||||
* {@link #exchangeStrategies(ExchangeStrategies) underlying}
|
||||
|
|
Loading…
Reference in New Issue