From 0b2c503e3a5f21c177fbee7ddec2baf46429ad71 Mon Sep 17 00:00:00 2001 From: Michal Rowicki Date: Sun, 23 May 2021 13:21:31 +0200 Subject: [PATCH] Soft assertions for WebTestClient It happens very often that WebTestClient is used in heavyweight integration tests. It's no use to waste time to check if another condition has been fixed or not. Soft assertion could help a lot to check all conditions at once even if one of them fail. New API would look like as follows: ```java client.get().uri("/test") .exchange() .expectAllSoftly( exchange -> exchange.expectStatus().isOk(), exchange -> exchange.expectBody(String.class) .isEqualTo("It works!") ); ``` --- .../reactive/server/DefaultWebTestClient.java | 19 +++++ .../web/reactive/server/WebTestClient.java | 6 ++ .../server/samples/SoftAssertionTests.java | 75 +++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/SoftAssertionTests.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 8d0ec7de0c5d..06dab2ea38a6 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 @@ -21,6 +21,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; @@ -507,6 +508,24 @@ public FluxExchangeResult returnResult(ParameterizedTypeReference elem Flux body = this.response.bodyToFlux(elementTypeRef); return new FluxExchangeResult<>(this.exchangeResult, body); } + + @Override + public ResponseSpec expectAllSoftly(ResponseSpecMatcher... asserts) { + List failedMessages = new ArrayList<>(); + for (int i = 0; i < asserts.length; i++) { + ResponseSpecMatcher anAssert = asserts[i]; + try { + anAssert.accept(this); + } + catch (AssertionError assertionException) { + failedMessages.add("[" + i + "] " + assertionException.getMessage()); + } + } + if (!failedMessages.isEmpty()) { + throw new AssertionError(String.join("\n", failedMessages)); + } + 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 6d2fcefebd70..8f0159aa70ad 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 @@ -845,6 +845,11 @@ interface ResponseSpec { * about a target type with generics. */ FluxExchangeResult returnResult(ParameterizedTypeReference elementTypeRef); + + /** + * Array of assertions to test together a.k.a. soft assertions. + */ + ResponseSpec expectAllSoftly(ResponseSpecMatcher... asserts); } @@ -1006,4 +1011,5 @@ default XpathAssertions xpath(String expression, Object... args) { EntityExchangeResult returnResult(); } + interface ResponseSpecMatcher extends Consumer {} } diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/SoftAssertionTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/SoftAssertionTests.java new file mode 100644 index 000000000000..17d5cc1a3d34 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/SoftAssertionTests.java @@ -0,0 +1,75 @@ +/* + * 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. + * 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.samples; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +/** + * Samples of tests using {@link WebTestClient} with soft assertions. + * + * @author MichaƂ Rowicki + * @since 5.3 + */ +public class SoftAssertionTests { + + private WebTestClient client; + + + @BeforeEach + public void setUp() throws Exception { + this.client = WebTestClient.bindToController(new TestController()).build(); + } + + + @Test + public void test() throws Exception { + this.client.get().uri("/test") + .exchange() + .expectAllSoftly( + exchange -> exchange.expectStatus().isOk(), + exchange -> exchange.expectBody(String.class).isEqualTo("It works!") + ); + } + + @Test + public void testAllFails() throws Exception { + assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> + this.client.get().uri("/test") + .exchange() + .expectAllSoftly( + exchange -> exchange.expectStatus().isBadRequest(), + exchange -> exchange.expectBody(String.class).isEqualTo("It won't work :(") + ) + ).withMessage("[0] Status expected:<400 BAD_REQUEST> but was:<200 OK>\n[1] Response body expected: but was:"); + } + + + @RestController + static class TestController { + + @GetMapping("/test") + public String handle() { + return "It works!"; + } + } +}