I'm writing a test and I want to capture the messages sent on STDOUT by the tested method. Here is my code:
@Test
public void testAction() throws IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException,
CmdLineException, IOException {
PrintStream originalSTDOUT = System.out;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
try {
System.setOut(ps);
// Call to the method that will print text to STDOUT...
ps.flush();
String batchLog = baos.toString("UTF-8");
assertTrue("Invalid exception text !", batchLog.contains("my expected text..."));
} finally {
System.setOut(originalSTDOUT);//Restore original STDOUT
originalSTDOUT.write(baos.toByteArray());// Write back to STDOUT, even if I comment this line, outputs go to STDOUT...
ps.close();
}
}
Unfortunately, batchLog
is always empty although I can still read the expected text to STDOUT.
The method that will print text to STDOUT in fact catch an exception. It then passes it to a Logger
like below:
log.warn("An error has occured:", e);
where e
is the exception raised in the method I call in my test.
The Logger
uses this appender for printing its messages: org.apache.log4j.ConsoleAppender
. No special configuration is applied to this appender.
What am I missing ?
P.S.:
This SO answer is interesting but it doesn't work for me since the ConsoleAppender
is already created before the StandardOutputStreamLog rule can act.
Java 6
Junit 4.10
I finally solved my problem !
My first attempt was wrong because I redirect the STDOUT too late as @Joni stated in his comments: ConsoleAppender
is already created.
Since, the exception message is handled by a Logger
, I decided to add a WriterAppender
backed by a StringWriter
to it in order to get the message.
Here is my working solution:
@Test
public void testAction() throws IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException,
CmdLineException, IOException {
// Setup WriterAppender
Writer w = new StringWriter();
Layout l = new PatternLayout("%m%n");
WriterAppender wa = new WriterAppender(l, w);
wa.setEncoding("UTF-8");
wa.setThreshold(Level.ALL);
wa.activateOptions();// WriterAppender does nothing here, but I like defensive code...
// Add it to logger
Logger log = Logger.getLogger(ExceptionHandler.class);// ExceptionHandler is the class that contains this code : `log.warn("An error has occured:", e);'
log.addAppender(wa);
try {
// Call to the method that will print text to STDOUT...
String batchLog = w.toString();
assertTrue("Invalid exception text !", batchLog.contains("my expected text..."));
} finally {
// Cleanup everything...
log.removeAppender(wa);
wa.close();
}
}
Slightly off topic, but in case some people (like me, when I first found this thread) might be interested in capturing log output via SLF4J, commons-testing's JUnit @Rule
might help:
public class FooTest {
@Rule
public final ExpectedLogs logs = new ExpectedLogs() {{
captureFor(Foo.class, LogLevel.WARN);
}};
@Test
public void barShouldLogWarning() {
assertThat(logs.isEmpty(), is(true)); // Nothing captured yet.
// Logic using the class you are capturing logs for:
Foo foo = new Foo();
assertThat(foo.bar(), is(not(nullValue())));
// Assert content of the captured logs:
assertThat(logs.isEmpty(), is(false));
assertThat(logs.contains("Your warning message here"), is(true));
}
}
Disclaimer:
log4j
, log4j2
and logback
are available at the moment, but I am happy to add more.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With