Skip to content

Commit 693dcba

Browse files
committed
Introduce LoggingResultHandler in Spring MVC Test
Prior to this commit, the Spring MVC Test framework only provided support for printing debug information about the MvcResult to STDOUT. This commit introduces support for logging `MvcResult` details at `DEBUG` level via the Apache Commons Logging API. In addition, this commit introduces additional `print(..)` variants for printing debug information to custom output streams and writers. Specifically, `MockMvcResultHandlers` has been augmented with the following new static methods: - `log()` - `print(OutputStream)` - `print(Writer)` Issue: SPR-13171
1 parent 895d43a commit 693dcba

File tree

6 files changed

+138
-32
lines changed

6 files changed

+138
-32
lines changed

spring-test/src/main/java/org/springframework/test/web/servlet/result/MockMvcResultHandlers.java

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616

1717
package org.springframework.test.web.servlet.result;
1818

19+
import java.io.OutputStream;
20+
import java.io.PrintWriter;
21+
import java.io.StringWriter;
22+
import java.io.Writer;
23+
24+
import org.apache.commons.logging.Log;
25+
import org.apache.commons.logging.LogFactory;
26+
1927
import org.springframework.test.web.servlet.MvcResult;
2028
import org.springframework.test.web.servlet.ResultHandler;
2129
import org.springframework.util.CollectionUtils;
@@ -32,35 +40,100 @@
3240
*/
3341
public abstract class MockMvcResultHandlers {
3442

43+
private static final Log logger = LogFactory.getLog(MockMvcResultHandlers.class.getPackage().getName());
44+
45+
46+
/**
47+
* Log {@link MvcResult} details as a {@code DEBUG} log message via
48+
* Apache Commons Logging using the log category
49+
* {@code org.springframework.test.web.servlet.result}.
50+
* @since 4.2
51+
* @see #print()
52+
* @see #print(OutputStream)
53+
* @see #print(Writer)
54+
*/
55+
public static ResultHandler log() {
56+
return new LoggingResultHandler();
57+
}
58+
3559
/**
3660
* Print {@link MvcResult} details to the "standard" output stream.
61+
* @see System#out
62+
* @see #print(OutputStream)
63+
* @see #print(Writer)
64+
* @see #log()
3765
*/
3866
public static ResultHandler print() {
39-
return new ConsolePrintingResultHandler();
67+
return print(System.out);
4068
}
4169

70+
/**
71+
* Print {@link MvcResult} details to the supplied {@link OutputStream}.
72+
* @since 4.2
73+
* @see #print()
74+
* @see #print(Writer)
75+
* @see #log()
76+
*/
77+
public static ResultHandler print(OutputStream stream) {
78+
return new PrintWriterPrintingResultHandler(new PrintWriter(stream, true));
79+
}
4280

4381
/**
44-
* An {@link PrintingResultHandler} that writes to the "standard" output stream
82+
* Print {@link MvcResult} details to the supplied {@link Writer}.
83+
* @since 4.2
84+
* @see #print()
85+
* @see #print(OutputStream)
86+
* @see #log()
4587
*/
46-
private static class ConsolePrintingResultHandler extends PrintingResultHandler {
88+
public static ResultHandler print(Writer writer) {
89+
return new PrintWriterPrintingResultHandler(new PrintWriter(writer, true));
90+
}
91+
4792

48-
public ConsolePrintingResultHandler() {
93+
/**
94+
* A {@link PrintingResultHandler} that writes to a {@link PrintWriter}.
95+
*/
96+
private static class PrintWriterPrintingResultHandler extends PrintingResultHandler {
97+
98+
PrintWriterPrintingResultHandler(final PrintWriter writer) {
4999
super(new ResultValuePrinter() {
50100
@Override
51101
public void printHeading(String heading) {
52-
System.out.println();
53-
System.out.println(String.format("%s:", heading));
102+
writer.println();
103+
writer.println(String.format("%s:", heading));
54104
}
55105
@Override
56106
public void printValue(String label, Object value) {
57107
if (value != null && value.getClass().isArray()) {
58108
value = CollectionUtils.arrayToList(value);
59109
}
60-
System.out.println(String.format("%17s = %s", label, value));
110+
writer.println(String.format("%17s = %s", label, value));
61111
}
62112
});
63113
}
64114
}
65115

116+
/**
117+
* A {@link ResultHandler} that logs {@link MvcResult} details at
118+
* {@code DEBUG} level via Apache Commons Logging.
119+
*
120+
* <p>Delegates to a {@link PrintWriterPrintingResultHandler} for
121+
* building the log message.
122+
* @since 4.2
123+
*/
124+
private static class LoggingResultHandler implements ResultHandler {
125+
126+
private final StringWriter stringWriter = new StringWriter();
127+
128+
private final ResultHandler printingResultHandler = new PrintWriterPrintingResultHandler(
129+
new PrintWriter(stringWriter, true));
130+
131+
132+
@Override
133+
public void handle(MvcResult result) throws Exception {
134+
this.printingResultHandler.handle(result);
135+
logger.debug("MvcResult details:\n" + this.stringWriter);
136+
}
137+
}
138+
66139
}

spring-test/src/main/java/org/springframework/test/web/servlet/result/PrintingResultHandler.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,13 @@
4040
import org.springframework.web.servlet.support.RequestContextUtils;
4141

4242
/**
43-
* Result handler that prints {@link MvcResult} details to the "standard" output
44-
* stream.
45-
* <p>An instance of this class is typically accessed via
46-
* {@link MockMvcResultHandlers#print()}.
43+
* Result handler that prints {@link MvcResult} details to a given output
44+
* stream &mdash; for example: {@code System.out}, {@code System.err}, a
45+
* custom {@code java.io.PrintWriter}, etc.
46+
*
47+
* <p>An instance of this class is typically accessed via one of the
48+
* {@link MockMvcResultHandlers#print print} or {@link MockMvcResultHandlers#log log}
49+
* methods in {@link MockMvcResultHandlers}.
4750
*
4851
* @author Rossen Stoyanchev
4952
* @author Sam Brannen
@@ -70,7 +73,7 @@ protected ResultValuePrinter getPrinter() {
7073
}
7174

7275
/**
73-
* Print {@link MvcResult} details to the "standard" output stream.
76+
* Print {@link MvcResult} details.
7477
*/
7578
@Override
7679
public final void handle(MvcResult result) throws Exception {

spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@
4242
import static org.junit.Assert.*;
4343

4444
/**
45-
* Tests for {@link PrintingResultHandler}.
45+
* Unit tests for {@link PrintingResultHandler}.
4646
*
4747
* @author Rossen Stoyanchev
4848
* @author Sam Brannen
49+
* @see org.springframework.test.web.servlet.samples.standalone.resulthandlers.PrintingResultHandlerSmokeTests
4950
*/
5051
public class PrintingResultHandlerTests {
5152

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616

1717
package org.springframework.test.web.servlet.samples.standalone.resulthandlers;
1818

19+
import java.io.StringWriter;
20+
1921
import javax.servlet.http.Cookie;
2022
import javax.servlet.http.HttpServletResponse;
2123

2224
import org.junit.Ignore;
2325
import org.junit.Test;
2426

2527
import org.springframework.stereotype.Controller;
28+
import org.springframework.test.web.servlet.result.PrintingResultHandler;
2629
import org.springframework.web.bind.annotation.RequestMapping;
2730
import org.springframework.web.bind.annotation.ResponseBody;
2831

@@ -31,17 +34,38 @@
3134
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
3235

3336
/**
34-
* Print debugging information about the executed request and response to System.out.
37+
* Smoke test for {@link PrintingResultHandler}.
38+
*
39+
* <p>Prints debugging information about the executed request and response to
40+
* various output streams.
41+
*
42+
* <p><strong>NOTE</strong>: this <em>smoke test</em> is not intended to be
43+
* executed with the build. To run this test, comment out the {@code @Ignore}
44+
* declaration and inspect the output manually.
3545
*
3646
* @author Rossen Stoyanchev
3747
* @author Sam Brannen
48+
* @see org.springframework.test.web.servlet.result.PrintingResultHandlerTests
3849
*/
3950
@Ignore("Not intended to be executed with the build. Comment out this line to inspect the output manually.")
40-
public class PrintingResultHandlerTests {
51+
public class PrintingResultHandlerSmokeTests {
4152

4253
@Test
4354
public void testPrint() throws Exception {
44-
standaloneSetup(new SimpleController()).build().perform(get("/")).andDo(print());
55+
StringWriter writer = new StringWriter();
56+
57+
standaloneSetup(new SimpleController())
58+
.build()
59+
.perform(get("/"))
60+
.andDo(log())
61+
.andDo(print())
62+
.andDo(print(System.err))
63+
.andDo(print(writer))
64+
;
65+
66+
System.out.println();
67+
System.out.println("===============================================================");
68+
System.out.println(writer.toString());
4569
}
4670

4771

spring-test/src/test/resources/log4j.properties

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,29 @@ log4j.appender.console.layout=org.apache.log4j.PatternLayout
33
log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%-5p] [%c] - %m%n
44

55
log4j.appender.file=org.apache.log4j.FileAppender
6-
log4j.appender.file.file=build/spring-test.log
6+
log4j.appender.file.file=bin/spring-test.log
77
log4j.appender.file.layout=org.apache.log4j.PatternLayout
88
log4j.appender.file.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%c] - %m%n
99

1010
log4j.rootCategory=ERROR, console, file
1111

1212
log4j.logger.org.springframework.beans=WARN
1313

14+
log4j.logger.org.springframework.test.context=WARN
1415
log4j.logger.org.springframework.test.context.TestContext=WARN
1516
log4j.logger.org.springframework.test.context.TestContextManager=WARN
1617
log4j.logger.org.springframework.test.context.ContextLoaderUtils=WARN
17-
log4j.logger.org.springframework.test.context.transaction.TransactionalTestExecutionListener=WARN
18-
log4j.logger.org.springframework.test.context.web=WARN
19-
log4j.logger.org.springframework.test.context=WARN
2018
log4j.logger.org.springframework.test.context.cache=WARN
21-
2219
log4j.logger.org.springframework.test.context.junit4.rules=WARN
20+
log4j.logger.org.springframework.test.context.transaction.TransactionalTestExecutionListener=WARN
21+
log4j.logger.org.springframework.test.context.web=WARN
2322

2423
#log4j.logger.org.springframework.test.context.support=INFO
2524
#log4j.logger.org.springframework.test.context.support.DelegatingSmartContextLoader=INFO
2625
#log4j.logger.org.springframework.test.context.support.AbstractGenericContextLoader=INFO
2726
#log4j.logger.org.springframework.test.context.support.AnnotationConfigContextLoader=INFO
2827

28+
log4j.logger.org.springframework.test.web.servlet.result=DEBUG
29+
2930
#log4j.logger.org.springframework.test=TRACE
3031

src/asciidoc/whats-new.adoc

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -554,14 +554,9 @@ public @interface MyTestConfig {
554554
@Rule
555555
public final SpringMethodRule springMethodRule = new SpringMethodRule();
556556
----
557-
* The `ContextCache` that is used for caching ++ApplicationContext++s
558-
between tests is now a public API with a default implementation that
559-
can be replaced for custom caching needs.
560-
* `DefaultTestContext`, `DefaultBootstrapContext`, and
561-
`DefaultCacheAwareContextLoaderDelegate` are now public classes in the
562-
`support` subpackage, allowing for custom extensions.
563-
* ++TestContextBootstrapper++s are now responsible for building the
564-
`TestContext`.
557+
* `AopTestUtils` is a new testing utility that allows developers to
558+
obtain a reference to the underlying target object hidden behind one
559+
or more Spring proxies.
565560
* `ReflectionTestUtils` now supports setting and getting `static` fields,
566561
including constants.
567562
* The original ordering of bean definition profiles declared via
@@ -575,6 +570,18 @@ public @interface MyTestConfig {
575570
configuration for the `ApplicationContext`.
576571
* `@Sql` now supports execution of _inlined SQL statements_ via a new
577572
`statements` attribute.
573+
* The `ContextCache` that is used for caching ++ApplicationContext++s
574+
between tests is now a public API with a default implementation that
575+
can be replaced for custom caching needs.
576+
* `DefaultTestContext`, `DefaultBootstrapContext`, and
577+
`DefaultCacheAwareContextLoaderDelegate` are now public classes in the
578+
`support` subpackage, allowing for custom extensions.
579+
* ++TestContextBootstrapper++s are now responsible for building the
580+
`TestContext`.
581+
* In the Spring MVC Test framework, `MvcResult` details can now be logged
582+
at `DEBUG` level or written to a custom `OutputStream` or `Writer`. See
583+
the new `log()`, `print(OutputStream)`, and `print(Writer)` methods in
584+
`MockMvcResultHandlers` for details.
578585
* The JDBC XML namespace supports a new `database-name` attribute in
579586
`<jdbc:embedded-database>`, allowing developers to set unique names
580587
for embedded databases –- for example, via a SpEL expression or a
@@ -587,6 +594,3 @@ public @interface MyTestConfig {
587594
** `EmbeddedDatabaseFactory.setGenerateUniqueDatabaseName()`
588595
** `EmbeddedDatabaseBuilder.generateUniqueName()`
589596
** `<jdbc:embedded-database generate-name="true" ... >`
590-
* `AopTestUtils` is a new testing utility that allows developers to
591-
obtain a reference to the underlying target object hidden behind one
592-
or more Spring proxies.

0 commit comments

Comments
 (0)