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
This commit is contained in:
Sam Brannen 2015-06-27 02:50:07 +02:00
parent a2d3c27ed1
commit 895d43a2b3
4 changed files with 97 additions and 46 deletions

View File

@ -24,9 +24,10 @@ import org.springframework.util.CollectionUtils;
* Static, factory methods for {@link ResultHandler}-based result actions.
*
* <p><strong>Eclipse users:</strong> 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));
}
});
}

View File

@ -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.
* <p>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) {

View File

@ -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<String, Map<String, Object>> 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);

View File

@ -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";
}
}