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 bbcb0a2e127d..8259a19508b7 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * 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. @@ -32,9 +32,11 @@ public interface ResultActions { /** * Perform an expectation. * - *

Example

+ *

Examples

+ * + *

You can invoke {@code andExpect()} multiple times. *

-	 * static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*
+	 * // static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*
 	 *
 	 * mockMvc.perform(get("/person/1"))
 	 *   .andExpect(status().isOk())
@@ -42,9 +44,9 @@ public interface ResultActions {
 	 *   .andExpect(jsonPath("$.person.name").value("Jason"));
 	 * 
* - *

Either provide all matchers as a vararg: + *

You can provide all matchers as a var-arg list with {@code matchAll()}. *

-	 * static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*, ResultMatcher.matchAll
+	 * // static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*, ResultMatcher.matchAll
 	 *
 	 * mockMvc.perform(post("/form"))
 	 *   .andExpect(matchAll(
@@ -57,13 +59,14 @@ public interface ResultActions {
 	 *   );
 	 * 
* - *

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

Alternatively, you can provide all matchers to be evaluated using + * soft assertions with {@code matchAllSoftly()}. *

-	 * static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*, ResultMatcher.matchAllSoftly
+	 * // static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*, ResultMatcher.matchAllSoftly
 	 * mockMvc.perform(post("/form"))
 	 *   .andExpect(matchAllSoftly(
 	 *       status().isOk(),
-	 *       redirectedUrl("/person/1")
+	 *       redirectedUrl("/person/1"))
 	 *   );
 	 * 
*/ 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 6c4d054d2359..13624102988f 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * 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. @@ -16,9 +16,6 @@ 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. @@ -47,6 +44,7 @@ * * @author Rossen Stoyanchev * @author Sam Brannen + * @author Michał Rowicki * @since 3.2 */ @FunctionalInterface @@ -74,27 +72,29 @@ 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. + * Static method for matching with an array of result matchers whose assertion + * failures are caught and stored. Once all matchers have been called, if any + * failures occurred, an {@link AssertionError} will be thrown containing the + * error messages of all assertion failures. * @param matchers the matchers - * @author Michał Rowicki - * @since 5.2 + * @since 5.3.10 */ static ResultMatcher matchAllSoftly(ResultMatcher... matchers) { return result -> { - List failedMessages = new ArrayList<>(); - for (int i = 0; i < matchers.length; i++) { - ResultMatcher matcher = matchers[i]; + String message = ""; + for (ResultMatcher matcher : matchers) { try { matcher.match(result); } - catch (AssertionError assertionException) { - failedMessages.add("[" + i + "] " + assertionException.getMessage()); + catch (Error | Exception ex) { + if (!message.isEmpty()) { + message += System.lineSeparator(); + } + message += ex.getMessage(); } } - if (!failedMessages.isEmpty()) { - throw new AssertionError(String.join("\n", failedMessages)); + if (!message.isEmpty()) { + throw new AssertionError(message); } }; } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/ResultMatcherTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/ResultMatcherTests.java index 40cbb56063f9..17ff0588269f 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/ResultMatcherTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/ResultMatcherTests.java @@ -16,51 +16,62 @@ package org.springframework.test.web.servlet; -import org.jetbrains.annotations.NotNull; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatNoException; +/** + * Unit tests for {@link ResultMatcher}. + * + * @author Michał Rowicki + * @author Sam Brannen + * @since 5.3.10 + */ class ResultMatcherTests { + private final StubMvcResult stubMvcResult = new StubMvcResult(null, null, null, null, null, null, null); + + @Test - void whenProvidedMatcherPassesThenSoftAssertionsAlsoPasses() { + void softAssertionsWithNoFailures() { 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)); + void softAssertionsWithOneFailure() { + String failureMessage = "failure message"; + ResultMatcher resultMatcher = ResultMatcher.matchAllSoftly(failingMatcher(failureMessage)); assertThatExceptionOfType(AssertionError.class) .isThrownBy(() -> resultMatcher.match(stubMvcResult)) - .withMessage("[0] " + failMessage); + .withMessage(failureMessage); } @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)); + void softAssertionsWithTwoFailures() { + String firstFailure = "firstFailure"; + String secondFailure = "secondFailure"; + ResultMatcher resultMatcher = ResultMatcher.matchAllSoftly(failingMatcher(firstFailure), exceptionalMatcher(secondFailure)); assertThatExceptionOfType(AssertionError.class) .isThrownBy(() -> resultMatcher.match(stubMvcResult)) - .withMessage("[0] " + firstFail + "\n[1] " + secondFail); + .withMessage(firstFailure + System.lineSeparator() + secondFailure); } - @NotNull - private ResultMatcher failMatcher(String failMessage) { + private ResultMatcher failingMatcher(String failureMessage) { + return result -> Assertions.fail(failureMessage); + } + + private ResultMatcher exceptionalMatcher(String failureMessage) { return result -> { - throw new AssertionError(failMessage); + throw new RuntimeException(failureMessage); }; } void doNothing(MvcResult mvcResult) {} + }