diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/ResultActions.java b/spring-test/src/main/java/org/springframework/test/web/servlet/ResultActions.java index d63a5dd4884e..bbcb0a2e127d 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/ResultActions.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/ResultActions.java @@ -42,7 +42,7 @@ public interface ResultActions { * .andExpect(jsonPath("$.person.name").value("Jason")); * * - *

Or alternatively provide all matchers as a vararg: + *

Either provide all matchers as a vararg: *

 	 * static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*, ResultMatcher.matchAll
 	 *
@@ -56,6 +56,16 @@ public interface ResultActions {
 	 *       flash().attribute("message", "success!"))
 	 *   );
 	 * 
+ * + *

Or provide all matchers to be evaluated no matter if one of them fail: + *

+	 * static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*, ResultMatcher.matchAllSoftly
+	 * mockMvc.perform(post("/form"))
+	 *   .andExpect(matchAllSoftly(
+	 *       status().isOk(),
+	 *       redirectedUrl("/person/1")
+	 *   );
+	 * 
*/ ResultActions andExpect(ResultMatcher matcher) throws Exception; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/ResultMatcher.java b/spring-test/src/main/java/org/springframework/test/web/servlet/ResultMatcher.java index 1b6b409806d4..870da6ebdc54 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/ResultMatcher.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/ResultMatcher.java @@ -16,6 +16,9 @@ package org.springframework.test.web.servlet; +import java.util.ArrayList; +import java.util.List; + /** * A {@code ResultMatcher} matches the result of an executed request against * some expectation. @@ -70,4 +73,32 @@ static ResultMatcher matchAll(ResultMatcher... matchers) { }; } + /** + * Static method for matching with an array of result matchers whose assertion failures are caught and stored. + * Only when all of them would be called a {@link AssertionError} be thrown containing the error messages of those + * previously caught assertion failures. + * @param matchers the matchers + * @author MichaƂ Rowicki + * @since 5.2 + */ + static ResultMatcher matchAllSoftly(ResultMatcher... matchers) { + return result -> { + List failedMessages = null; + for (ResultMatcher matcher : matchers) { + try { + matcher.match(result); + } + catch (AssertionError assertionException) { + if (failedMessages == null) { + failedMessages = new ArrayList<>(); + } + failedMessages.add(assertionException.getMessage()); + } + } + if (failedMessages != null) { + throw new AssertionError(String.join("\n", failedMessages)); + } + }; + } + } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/ResultMatcherTest.java b/spring-test/src/test/java/org/springframework/test/web/servlet/ResultMatcherTest.java new file mode 100644 index 000000000000..02f1b9f3716f --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/ResultMatcherTest.java @@ -0,0 +1,50 @@ +package org.springframework.test.web.servlet; + +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; + +class ResultMatcherTest { + + @Test + void whenProvidedMatcherPassesThenSoftAssertionsAlsoPasses() { + ResultMatcher resultMatcher = ResultMatcher.matchAllSoftly(this::doNothing); + StubMvcResult stubMvcResult = new StubMvcResult(null, null, null, null, null, null, null); + + assertThatNoException().isThrownBy(() -> resultMatcher.match(stubMvcResult)); + } + + @Test + void whenOneOfMatcherFailsThenSoftAssertionFailsWithTheVerySameMessage() { + String failMessage = "fail message"; + StubMvcResult stubMvcResult = new StubMvcResult(null, null, null, null, null, null, null); + ResultMatcher resultMatcher = ResultMatcher.matchAllSoftly(failMatcher(failMessage)); + + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> resultMatcher.match(stubMvcResult)) + .withMessage(failMessage); + } + + @Test + void whenMultipleMatchersFailsThenSoftAssertionFailsWithOneErrorWithMessageContainingAllErrorMessagesWithTheSameOrder() { + String firstFail = "firstFail"; + String secondFail = "secondFail"; + StubMvcResult stubMvcResult = new StubMvcResult(null, null, null, null, null, null, null); + ResultMatcher resultMatcher = ResultMatcher.matchAllSoftly(failMatcher(firstFail), failMatcher(secondFail)); + + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> resultMatcher.match(stubMvcResult)) + .withMessage(firstFail + "\n" + secondFail); + } + + @NotNull + private ResultMatcher failMatcher(String failMessage) { + return result -> { + throw new AssertionError(failMessage); + }; + } + + void doNothing(MvcResult mvcResult) {} +} \ No newline at end of file