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!"; + } + } +}