Skip to content

Commit 25dca40

Browse files
wyhasanysbrannen
authored andcommitted
Introduce soft assertions for WebTestClient
It happens very often that WebTestClient is used in heavyweight integration tests, and it's a hindrance to developer productivity to fix one failed assertion after another. Soft assertions help a lot by checking all conditions at once even if one of them fails. This commit introduces a new expectAllSoftly(..) method in WebTestClient to address this issue. client.get().uri("/hello") .exchange() .expectAllSoftly( spec -> spec.expectStatus().isOk(), spec -> spec.expectBody(String.class).isEqualTo("Hello, World") ); Closes gh-26969
1 parent dd9b99e commit 25dca40

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed

spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.nio.charset.StandardCharsets;
2222
import java.time.Duration;
2323
import java.time.ZonedDateTime;
24+
import java.util.ArrayList;
2425
import java.util.Arrays;
2526
import java.util.LinkedHashMap;
2627
import java.util.List;
@@ -507,6 +508,24 @@ public <T> FluxExchangeResult<T> returnResult(ParameterizedTypeReference<T> elem
507508
Flux<T> body = this.response.bodyToFlux(elementTypeRef);
508509
return new FluxExchangeResult<>(this.exchangeResult, body);
509510
}
511+
512+
@Override
513+
public ResponseSpec expectAllSoftly(ResponseSpecMatcher... asserts) {
514+
List<String> failedMessages = new ArrayList<>();
515+
for (int i = 0; i < asserts.length; i++) {
516+
ResponseSpecMatcher anAssert = asserts[i];
517+
try {
518+
anAssert.accept(this);
519+
}
520+
catch (AssertionError assertionException) {
521+
failedMessages.add("[" + i + "] " + assertionException.getMessage());
522+
}
523+
}
524+
if (!failedMessages.isEmpty()) {
525+
throw new AssertionError(String.join("\n", failedMessages));
526+
}
527+
return this;
528+
}
510529
}
511530

512531

spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,11 @@ interface ResponseSpec {
845845
* about a target type with generics.
846846
*/
847847
<T> FluxExchangeResult<T> returnResult(ParameterizedTypeReference<T> elementTypeRef);
848+
849+
/**
850+
* Array of assertions to test together a.k.a. soft assertions.
851+
*/
852+
ResponseSpec expectAllSoftly(ResponseSpecMatcher... asserts);
848853
}
849854

850855

@@ -1006,4 +1011,5 @@ default XpathAssertions xpath(String expression, Object... args) {
10061011
EntityExchangeResult<byte[]> returnResult();
10071012
}
10081013

1014+
interface ResponseSpecMatcher extends Consumer<ResponseSpec> {}
10091015
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.test.web.reactive.server.samples;
17+
18+
import org.junit.jupiter.api.BeforeEach;
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.test.web.reactive.server.WebTestClient;
22+
import org.springframework.web.bind.annotation.GetMapping;
23+
import org.springframework.web.bind.annotation.RestController;
24+
25+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
26+
27+
/**
28+
* Samples of tests using {@link WebTestClient} with soft assertions.
29+
*
30+
* @author Michał Rowicki
31+
* @since 5.3
32+
*/
33+
public class SoftAssertionTests {
34+
35+
private WebTestClient client;
36+
37+
38+
@BeforeEach
39+
public void setUp() throws Exception {
40+
this.client = WebTestClient.bindToController(new TestController()).build();
41+
}
42+
43+
44+
@Test
45+
public void test() throws Exception {
46+
this.client.get().uri("/test")
47+
.exchange()
48+
.expectAllSoftly(
49+
exchange -> exchange.expectStatus().isOk(),
50+
exchange -> exchange.expectBody(String.class).isEqualTo("It works!")
51+
);
52+
}
53+
54+
@Test
55+
public void testAllFails() throws Exception {
56+
assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
57+
this.client.get().uri("/test")
58+
.exchange()
59+
.expectAllSoftly(
60+
exchange -> exchange.expectStatus().isBadRequest(),
61+
exchange -> exchange.expectBody(String.class).isEqualTo("It won't work :(")
62+
)
63+
).withMessage("[0] Status expected:<400 BAD_REQUEST> but was:<200 OK>\n[1] Response body expected:<It won't work :(> but was:<It works!>");
64+
}
65+
66+
67+
@RestController
68+
static class TestController {
69+
70+
@GetMapping("/test")
71+
public String handle() {
72+
return "It works!";
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)