From 895d43a2b3a80c88390345fd2c135d1c9d1cc7d2 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 27 Jun 2015 02:50:07 +0200 Subject: [PATCH] Print cookies in human-readable form in Spring MVC Test Prior to this commit, when rendering cookies via `andDo(print())` in Spring MVC Test, the output for the `MockHttpServletResponse` would look something like the following: Cookies = [javax.servlet.http.Cookie@25084a1e] The reason is that the Cookie class in javax.servlet-api-3.0.1.jar does not implement toString(). Consequently, nothing about the cookie's name, value, etc., is displayed, thereby making the debug output for cookies next to useless. This commit improves on this by implementing custom toString() logic for cookies in debug output in Spring MVC Test. For example, the output now looks like this (without the newlines): Cookies = [[Cookie@47faa49c name = 'enigma', value = '42', \\ comment = [null], domain = [null], maxAge = -1, \\ path = [null], secure = false, version = 0, \\ httpOnly = false]] In addition, this commit fixes a minor bug for FlashMap debug output if the FlashMap is empty. Issue: SPR-13168 --- .../servlet/result/MockMvcResultHandlers.java | 7 +- .../servlet/result/PrintingResultHandler.java | 38 +++++++- .../result/PrintingResultHandlerTests.java | 90 +++++++++++-------- .../PrintingResultHandlerTests.java | 8 +- 4 files changed, 97 insertions(+), 46 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/MockMvcResultHandlers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/MockMvcResultHandlers.java index beb832ae940..1728324d4ba 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/MockMvcResultHandlers.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/MockMvcResultHandlers.java @@ -24,9 +24,10 @@ import org.springframework.util.CollectionUtils; * Static, factory methods for {@link ResultHandler}-based result actions. * *

Eclipse users: consider adding this class as a Java editor - * favorite. To navigate, open the Preferences and type "favorites". + * favorite. To navigate to this setting, open the Preferences and type "favorites". * * @author Rossen Stoyanchev + * @author Sam Brannen * @since 3.2 */ public abstract class MockMvcResultHandlers { @@ -49,14 +50,14 @@ public abstract class MockMvcResultHandlers { @Override public void printHeading(String heading) { System.out.println(); - System.out.println(String.format("%20s:", heading)); + System.out.println(String.format("%s:", heading)); } @Override public void printValue(String label, Object value) { if (value != null && value.getClass().isArray()) { value = CollectionUtils.arrayToList(value); } - System.out.println(String.format("%20s = %s", label, value)); + System.out.println(String.format("%17s = %s", label, value)); } }); } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/PrintingResultHandler.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/PrintingResultHandler.java index 9dba5b1c474..3398f128b14 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/PrintingResultHandler.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/PrintingResultHandler.java @@ -18,8 +18,11 @@ package org.springframework.test.web.servlet.result; import java.util.Enumeration; import java.util.Map; + +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; +import org.springframework.core.style.ToStringCreator; import org.springframework.http.HttpHeaders; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -27,6 +30,7 @@ import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultHandler; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.ObjectUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.web.method.HandlerMethod; @@ -37,10 +41,12 @@ import org.springframework.web.servlet.support.RequestContextUtils; /** * Result handler that prints {@link MvcResult} details to the "standard" output - * stream. An instance of this class is typically accessed via + * stream. + *

An instance of this class is typically accessed via * {@link MockMvcResultHandlers#print()}. * * @author Rossen Stoyanchev + * @author Sam Brannen * @since 3.2 */ public class PrintingResultHandler implements ResultHandler { @@ -57,7 +63,7 @@ public class PrintingResultHandler implements ResultHandler { } /** - * @return the result value printer. + * @return the result value printer */ protected ResultValuePrinter getPrinter() { return this.printer; @@ -198,7 +204,7 @@ public class PrintingResultHandler implements ResultHandler { * Print "output" flash attributes. */ protected void printFlashMap(FlashMap flashMap) throws Exception { - if (flashMap == null) { + if (ObjectUtils.isEmpty(flashMap)) { this.printer.printValue("Attributes", null); } else { @@ -220,7 +226,31 @@ public class PrintingResultHandler implements ResultHandler { this.printer.printValue("Body", response.getContentAsString()); this.printer.printValue("Forwarded URL", response.getForwardedUrl()); this.printer.printValue("Redirected URL", response.getRedirectedUrl()); - this.printer.printValue("Cookies", response.getCookies()); + printCookies(response.getCookies()); + } + + /** + * Print the supplied cookies in a human-readable form, assuming the + * {@link Cookie} implementation does not provide its own {@code toString()}. + * @since 4.2 + */ + private void printCookies(Cookie[] cookies) { + String[] cookieStrings = new String[cookies.length]; + for (int i = 0; i < cookies.length; i++) { + Cookie cookie = cookies[i]; + cookieStrings[i] = new ToStringCreator(cookie) + .append("name", cookie.getName()) + .append("value", cookie.getValue()) + .append("comment", cookie.getComment()) + .append("domain", cookie.getDomain()) + .append("maxAge", cookie.getMaxAge()) + .append("path", cookie.getPath()) + .append("secure", cookie.getSecure()) + .append("version", cookie.getVersion()) + .append("httpOnly", cookie.isHttpOnly()) + .toString(); + } + this.printer.printValue("Cookies", cookieStrings); } protected final HttpHeaders getResponseHeaders(MockHttpServletResponse response) { diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java index 752cb6b9277..4de65c53d53 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -19,9 +19,9 @@ package org.springframework.test.web.servlet.result; import java.net.URI; import java.util.HashMap; import java.util.Map; + import javax.servlet.http.Cookie; -import org.junit.Before; import org.junit.Test; import org.springframework.http.HttpHeaders; @@ -45,33 +45,27 @@ import static org.junit.Assert.*; * Tests for {@link PrintingResultHandler}. * * @author Rossen Stoyanchev + * @author Sam Brannen */ public class PrintingResultHandlerTests { - private TestPrintingResultHandler handler; + private final TestPrintingResultHandler handler = new TestPrintingResultHandler(); - private MockHttpServletRequest request; + private final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/") { + @Override + public boolean isAsyncStarted() { + return false; + } + }; - private MockHttpServletResponse response; + private final MockHttpServletResponse response = new MockHttpServletResponse(); - private StubMvcResult mvcResult; + private final StubMvcResult mvcResult = new StubMvcResult(this.request, null, null, + null, null, null, this.response); - @Before - public void setup() { - this.handler = new TestPrintingResultHandler(); - this.request = new MockHttpServletRequest("GET", "/") { - @Override - public boolean isAsyncStarted() { - return false; - } - }; - this.response = new MockHttpServletResponse(); - this.mvcResult = new StubMvcResult(this.request, null, null, null, null, null, this.response); - } - @Test - public void testPrintRequest() throws Exception { + public void printRequest() throws Exception { this.request.addParameter("param", "paramValue"); this.request.addHeader("header", "headerValue"); @@ -91,7 +85,15 @@ public class PrintingResultHandlerTests { @Test @SuppressWarnings("deprecation") - public void testPrintResponse() throws Exception { + public void printResponse() throws Exception { + Cookie enigmaCookie = new Cookie("enigma", "42"); + enigmaCookie.setComment("This is a comment"); + enigmaCookie.setHttpOnly(true); + enigmaCookie.setMaxAge(1234); + enigmaCookie.setDomain(".example.com"); + enigmaCookie.setPath("/crumbs"); + enigmaCookie.setSecure(true); + this.response.setStatus(400, "error"); this.response.addHeader("header", "headerValue"); this.response.setContentType("text/plain"); @@ -99,6 +101,7 @@ public class PrintingResultHandlerTests { this.response.setForwardedUrl("redirectFoo"); this.response.sendRedirect("/redirectFoo"); this.response.addCookie(new Cookie("cookie", "cookieValue")); + this.response.addCookie(enigmaCookie); this.handler.handle(this.mvcResult); @@ -107,17 +110,30 @@ public class PrintingResultHandlerTests { headers.setContentType(MediaType.TEXT_PLAIN); headers.setLocation(new URI("/redirectFoo")); - assertValue("MockHttpServletResponse", "Status", this.response.getStatus()); - assertValue("MockHttpServletResponse", "Error message", response.getErrorMessage()); - assertValue("MockHttpServletResponse", "Headers", headers); - assertValue("MockHttpServletResponse", "Content type", this.response.getContentType()); - assertValue("MockHttpServletResponse", "Body", this.response.getContentAsString()); - assertValue("MockHttpServletResponse", "Forwarded URL", this.response.getForwardedUrl()); - assertValue("MockHttpServletResponse", "Redirected URL", this.response.getRedirectedUrl()); + String heading = "MockHttpServletResponse"; + assertValue(heading, "Status", this.response.getStatus()); + assertValue(heading, "Error message", response.getErrorMessage()); + assertValue(heading, "Headers", headers); + assertValue(heading, "Content type", this.response.getContentType()); + assertValue(heading, "Body", this.response.getContentAsString()); + assertValue(heading, "Forwarded URL", this.response.getForwardedUrl()); + assertValue(heading, "Redirected URL", this.response.getRedirectedUrl()); + + Map> printedValues = this.handler.getPrinter().printedValues; + String[] cookies = (String[]) printedValues.get(heading).get("Cookies"); + assertEquals(2, cookies.length); + String cookie1 = cookies[0]; + String cookie2 = cookies[1]; + assertTrue(cookie1.startsWith("[" + Cookie.class.getSimpleName())); + assertTrue(cookie1.contains("name = 'cookie', value = 'cookieValue'")); + assertTrue(cookie1.endsWith("]")); + assertTrue(cookie2.startsWith("[" + Cookie.class.getSimpleName())); + assertTrue(cookie2.contains("name = 'enigma', value = '42', comment = 'This is a comment', domain = '.example.com', maxAge = 1234, path = '/crumbs', secure = true, version = 0, httpOnly = true")); + assertTrue(cookie2.endsWith("]")); } @Test - public void testPrintHandlerNull() throws Exception { + public void printHandlerNull() throws Exception { StubMvcResult mvcResult = new StubMvcResult(this.request, null, null, null, null, null, this.response); this.handler.handle(mvcResult); @@ -125,7 +141,7 @@ public class PrintingResultHandlerTests { } @Test - public void testPrintHandler() throws Exception { + public void printHandler() throws Exception { this.mvcResult.setHandler(new Object()); this.handler.handle(this.mvcResult); @@ -133,7 +149,7 @@ public class PrintingResultHandlerTests { } @Test - public void testPrintHandlerMethod() throws Exception { + public void printHandlerMethod() throws Exception { HandlerMethod handlerMethod = new HandlerMethod(this, "handle"); this.mvcResult.setHandler(handlerMethod); this.handler.handle(mvcResult); @@ -143,14 +159,14 @@ public class PrintingResultHandlerTests { } @Test - public void testResolvedExceptionNull() throws Exception { + public void resolvedExceptionNull() throws Exception { this.handler.handle(this.mvcResult); assertValue("Resolved Exception", "Type", null); } @Test - public void testResolvedException() throws Exception { + public void resolvedException() throws Exception { this.mvcResult.setResolvedException(new Exception()); this.handler.handle(this.mvcResult); @@ -158,7 +174,7 @@ public class PrintingResultHandlerTests { } @Test - public void testModelAndViewNull() throws Exception { + public void modelAndViewNull() throws Exception { this.handler.handle(this.mvcResult); assertValue("ModelAndView", "View name", null); @@ -167,7 +183,7 @@ public class PrintingResultHandlerTests { } @Test - public void testModelAndView() throws Exception { + public void modelAndView() throws Exception { BindException bindException = new BindException(new Object(), "target"); bindException.reject("errorCode"); @@ -186,14 +202,14 @@ public class PrintingResultHandlerTests { } @Test - public void testFlashMapNull() throws Exception { + public void flashMapNull() throws Exception { this.handler.handle(mvcResult); assertValue("FlashMap", "Type", null); } @Test - public void testFlashMap() throws Exception { + public void flashMap() throws Exception { FlashMap flashMap = new FlashMap(); flashMap.put("attrName", "attrValue"); this.request.setAttribute(DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP", flashMap); diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resulthandlers/PrintingResultHandlerTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resulthandlers/PrintingResultHandlerTests.java index 775185af74a..c1bb0deac23 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resulthandlers/PrintingResultHandlerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resulthandlers/PrintingResultHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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,6 +16,9 @@ package org.springframework.test.web.servlet.samples.standalone.resulthandlers; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + import org.junit.Ignore; import org.junit.Test; @@ -47,7 +50,8 @@ public class PrintingResultHandlerTests { @RequestMapping("/") @ResponseBody - public String hello() { + public String hello(HttpServletResponse response) { + response.addCookie(new Cookie("enigma", "42")); return "Hello world"; } }