From 881fa889fcdb1870e12beb7010408a316b30dc62 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Thu, 29 Jul 2021 19:18:18 +0200 Subject: [PATCH] Apply global ResultHandlers before ResultMatchers in MockMvc Prior to this commit, MockMvc applied global ResultMatchers before global ResultHandlers. This lead to unexpected scenarios where a failing matcher would prevent a handler from being applied. One concrete use case is `alwaysDo(print(System.err))` which should print out MockMvc results for debugging purposes. However, if MockMvc is configured with something like `alwaysExpect(content().string("?"))` and the expectation fails, the user will never see the expected debug output to help diagnose the problem. This commit addresses this issue by applying global ResultHandlers before ResultMatchers in MockMvc. Closes gh-27225 --- .../test/web/servlet/MockMvc.java | 8 +- ...PrintingResultHandlerIntegrationTests.java | 95 +++++++++++++++++++ 2 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resulthandlers/PrintingResultHandlerIntegrationTests.java diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java index a8565da45b..39492cbd0a 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.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. @@ -217,12 +217,12 @@ public final class MockMvc { } private void applyDefaultResultActions(MvcResult mvcResult) throws Exception { - for (ResultMatcher matcher : this.defaultResultMatchers) { - matcher.match(mvcResult); - } for (ResultHandler handler : this.defaultResultHandlers) { handler.handle(mvcResult); } + for (ResultMatcher matcher : this.defaultResultMatchers) { + matcher.match(mvcResult); + } } } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resulthandlers/PrintingResultHandlerIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resulthandlers/PrintingResultHandlerIntegrationTests.java new file mode 100644 index 0000000000..d2a66f5178 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resulthandlers/PrintingResultHandlerIntegrationTests.java @@ -0,0 +1,95 @@ +/* + * 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.servlet.samples.standalone.resulthandlers; + +import java.io.StringWriter; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +import org.junit.jupiter.api.Test; + +import org.springframework.test.web.servlet.result.PrintingResultHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +/** + * Integration tests for {@link PrintingResultHandler}. + * + * @author Sam Brannen + * @author Rossen Stoyanchev + * @since 5.3.10 + * @see PrintingResultHandlerSmokeTests + * @see org.springframework.test.web.servlet.result.PrintingResultHandlerTests + */ +class PrintingResultHandlerIntegrationTests { + + @Test + void printMvcResultsToWriter() throws Exception { + StringWriter writer = new StringWriter(); + + standaloneSetup(new SimpleController()) + .alwaysDo(print(writer)) + .build() + .perform(get("/").content("Hello Request".getBytes()).characterEncoding("ISO-8859-1")) + .andExpect(content().string("Hello Response")); + + assertThat(writer).asString() + .contains("Hello Request") + .contains("Hello Response") + .contains("Headers = [Set-Cookie:\"enigma=42\", Content-Type:\"text/plain;charset=ISO-8859-1\", Content-Length:\"14\"]"); + } + + @Test + void printMvcResultsToWriterWithFailingGlobalResultMatcher() throws Exception { + StringWriter writer = new StringWriter(); + + try { + standaloneSetup(new SimpleController()) + .alwaysDo(print(writer)) + .alwaysExpect(content().string("Boom!")) + .build() + .perform(get("/").content("Hello Request".getBytes()).characterEncoding("ISO-8859-1")); + } + catch (AssertionError error) { + assertThat(error).hasMessageContaining("Boom!"); + } + + assertThat(writer).asString() + .contains("Hello Request") + .contains("Hello Response") + .contains("Headers = [Set-Cookie:\"enigma=42\", Content-Type:\"text/plain;charset=ISO-8859-1\", Content-Length:\"14\"]"); + } + + + @RestController + private static class SimpleController { + + @GetMapping("/") + String hello(HttpServletResponse response) { + response.addCookie(new Cookie("enigma", "42")); + return "Hello Response"; + } + } + +}