Skip to content

Commit 5f47d3b

Browse files
committed
Polish soft assertions for MockMvc
See gh-26917
1 parent 35bec81 commit 5f47d3b

File tree

3 files changed

+55
-41
lines changed

3 files changed

+55
-41
lines changed

spring-test/src/main/java/org/springframework/test/web/servlet/ResultActions.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,19 +32,21 @@ public interface ResultActions {
3232
/**
3333
* Perform an expectation.
3434
*
35-
* <h4>Example</h4>
35+
* <h4>Examples</h4>
36+
*
37+
* <p>You can invoke {@code andExpect()} multiple times.
3638
* <pre class="code">
37-
* static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*
39+
* // static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*
3840
*
3941
* mockMvc.perform(get("/person/1"))
4042
* .andExpect(status().isOk())
4143
* .andExpect(content().contentType(MediaType.APPLICATION_JSON))
4244
* .andExpect(jsonPath("$.person.name").value("Jason"));
4345
* </pre>
4446
*
45-
* <p>Either provide all matchers as a vararg:
47+
* <p>You can provide all matchers as a var-arg list with {@code matchAll()}.
4648
* <pre class="code">
47-
* static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*, ResultMatcher.matchAll
49+
* // static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*, ResultMatcher.matchAll
4850
*
4951
* mockMvc.perform(post("/form"))
5052
* .andExpect(matchAll(
@@ -57,13 +59,14 @@ public interface ResultActions {
5759
* );
5860
* </pre>
5961
*
60-
* <p>Or provide all matchers to be evaluated no matter if one of them fail:
62+
* <p>Alternatively, you can provide all matchers to be evaluated using
63+
* <em>soft assertions</em> with {@code matchAllSoftly()}.
6164
* <pre class="code">
62-
* static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*, ResultMatcher.matchAllSoftly
65+
* // static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*, ResultMatcher.matchAllSoftly
6366
* mockMvc.perform(post("/form"))
6467
* .andExpect(matchAllSoftly(
6568
* status().isOk(),
66-
* redirectedUrl("/person/1")
69+
* redirectedUrl("/person/1"))
6770
* );
6871
* </pre>
6972
*/

spring-test/src/main/java/org/springframework/test/web/servlet/ResultMatcher.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,9 +16,6 @@
1616

1717
package org.springframework.test.web.servlet;
1818

19-
import java.util.ArrayList;
20-
import java.util.List;
21-
2219
/**
2320
* A {@code ResultMatcher} matches the result of an executed request against
2421
* some expectation.
@@ -47,6 +44,7 @@
4744
*
4845
* @author Rossen Stoyanchev
4946
* @author Sam Brannen
47+
* @author Michał Rowicki
5048
* @since 3.2
5149
*/
5250
@FunctionalInterface
@@ -74,27 +72,29 @@ static ResultMatcher matchAll(ResultMatcher... matchers) {
7472
}
7573

7674
/**
77-
* Static method for matching with an array of result matchers whose assertion failures are caught and stored.
78-
* Only when all of them would be called a {@link AssertionError} be thrown containing the error messages of those
79-
* previously caught assertion failures.
75+
* Static method for matching with an array of result matchers whose assertion
76+
* failures are caught and stored. Once all matchers have been called, if any
77+
* failures occurred, an {@link AssertionError} will be thrown containing the
78+
* error messages of all assertion failures.
8079
* @param matchers the matchers
81-
* @author Michał Rowicki
82-
* @since 5.2
80+
* @since 5.3.10
8381
*/
8482
static ResultMatcher matchAllSoftly(ResultMatcher... matchers) {
8583
return result -> {
86-
List<String> failedMessages = new ArrayList<>();
87-
for (int i = 0; i < matchers.length; i++) {
88-
ResultMatcher matcher = matchers[i];
84+
String message = "";
85+
for (ResultMatcher matcher : matchers) {
8986
try {
9087
matcher.match(result);
9188
}
92-
catch (AssertionError assertionException) {
93-
failedMessages.add("[" + i + "] " + assertionException.getMessage());
89+
catch (Error | Exception ex) {
90+
if (!message.isEmpty()) {
91+
message += System.lineSeparator();
92+
}
93+
message += ex.getMessage();
9494
}
9595
}
96-
if (!failedMessages.isEmpty()) {
97-
throw new AssertionError(String.join("\n", failedMessages));
96+
if (!message.isEmpty()) {
97+
throw new AssertionError(message);
9898
}
9999
};
100100
}

spring-test/src/test/java/org/springframework/test/web/servlet/ResultMatcherTests.java

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,51 +16,62 @@
1616

1717
package org.springframework.test.web.servlet;
1818

19-
import org.jetbrains.annotations.NotNull;
19+
import org.assertj.core.api.Assertions;
2020
import org.junit.jupiter.api.Test;
2121

2222
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2323
import static org.assertj.core.api.Assertions.assertThatNoException;
2424

25+
/**
26+
* Unit tests for {@link ResultMatcher}.
27+
*
28+
* @author Michał Rowicki
29+
* @author Sam Brannen
30+
* @since 5.3.10
31+
*/
2532
class ResultMatcherTests {
2633

34+
private final StubMvcResult stubMvcResult = new StubMvcResult(null, null, null, null, null, null, null);
35+
36+
2737
@Test
28-
void whenProvidedMatcherPassesThenSoftAssertionsAlsoPasses() {
38+
void softAssertionsWithNoFailures() {
2939
ResultMatcher resultMatcher = ResultMatcher.matchAllSoftly(this::doNothing);
30-
StubMvcResult stubMvcResult = new StubMvcResult(null, null, null, null, null, null, null);
3140

3241
assertThatNoException().isThrownBy(() -> resultMatcher.match(stubMvcResult));
3342
}
3443

3544
@Test
36-
void whenOneOfMatcherFailsThenSoftAssertionFailsWithTheVerySameMessage() {
37-
String failMessage = "fail message";
38-
StubMvcResult stubMvcResult = new StubMvcResult(null, null, null, null, null, null, null);
39-
ResultMatcher resultMatcher = ResultMatcher.matchAllSoftly(failMatcher(failMessage));
45+
void softAssertionsWithOneFailure() {
46+
String failureMessage = "failure message";
47+
ResultMatcher resultMatcher = ResultMatcher.matchAllSoftly(failingMatcher(failureMessage));
4048

4149
assertThatExceptionOfType(AssertionError.class)
4250
.isThrownBy(() -> resultMatcher.match(stubMvcResult))
43-
.withMessage("[0] " + failMessage);
51+
.withMessage(failureMessage);
4452
}
4553

4654
@Test
47-
void whenMultipleMatchersFailsThenSoftAssertionFailsWithOneErrorWithMessageContainingAllErrorMessagesWithTheSameOrder() {
48-
String firstFail = "firstFail";
49-
String secondFail = "secondFail";
50-
StubMvcResult stubMvcResult = new StubMvcResult(null, null, null, null, null, null, null);
51-
ResultMatcher resultMatcher = ResultMatcher.matchAllSoftly(failMatcher(firstFail), failMatcher(secondFail));
55+
void softAssertionsWithTwoFailures() {
56+
String firstFailure = "firstFailure";
57+
String secondFailure = "secondFailure";
58+
ResultMatcher resultMatcher = ResultMatcher.matchAllSoftly(failingMatcher(firstFailure), exceptionalMatcher(secondFailure));
5259

5360
assertThatExceptionOfType(AssertionError.class)
5461
.isThrownBy(() -> resultMatcher.match(stubMvcResult))
55-
.withMessage("[0] " + firstFail + "\n[1] " + secondFail);
62+
.withMessage(firstFailure + System.lineSeparator() + secondFailure);
5663
}
5764

58-
@NotNull
59-
private ResultMatcher failMatcher(String failMessage) {
65+
private ResultMatcher failingMatcher(String failureMessage) {
66+
return result -> Assertions.fail(failureMessage);
67+
}
68+
69+
private ResultMatcher exceptionalMatcher(String failureMessage) {
6070
return result -> {
61-
throw new AssertionError(failMessage);
71+
throw new RuntimeException(failureMessage);
6272
};
6373
}
6474

6575
void doNothing(MvcResult mvcResult) {}
76+
6677
}

0 commit comments

Comments
 (0)