JUnit test for System.out.println()
I need to write JUnit tests for an old application that's poorly designed and is writing a lot of error messages to standard output. When the getResponse(String request)
method behaves correctly it returns a XML response:
@BeforeClass
public static void setUpClass() throws Exception {
Properties queries = loadPropertiesFile("requests.properties");
Properties responses = loadPropertiesFile("responses.properties");
instance = new ResponseGenerator(queries, responses);
}
@Test
public void testGetResponse() {
String request = "<some>request</some>";
String expResult = "<some>response</some>";
String result = instance.getResponse(request);
assertEquals(expResult, result);
}
But when it gets malformed XML or does not understand the request it returns null
and writes some stuff to standard output.
Is there any way to assert console output in JUnit? To catch cases like:
System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());
using ByteArrayOutputStream and System.setXXX is simple:
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
private final PrintStream originalOut = System.out;
private final PrintStream originalErr = System.err;
@Before
public void setUpStreams() {
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));
}
@After
public void restoreStreams() {
System.setOut(originalOut);
System.setErr(originalErr);
}
sample test cases:
@Test
public void out() {
System.out.print("hello");
assertEquals("hello", outContent.toString());
}
@Test
public void err() {
System.err.print("hello again");
assertEquals("hello again", errContent.toString());
}
I used this code to test the command line option (asserting that -version outputs the version string, etc etc)
Edit:
Prior versions of this answer called System.setOut(null)
after the tests; This is the cause of NullPointerExceptions commenters refer to.
I know this is an old thread, but there is a nice library to do this: System Rules
Example from the docs:
public void MyTest {
@Rule
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();
@Test
public void overrideProperty() {
System.out.print("hello world");
assertEquals("hello world", systemOutRule.getLog());
}
}
It will also allow you to trap System.exit(-1)
and other things that a command line tool would need to be tested for.
Instead of redirecting System.out
, I would refactor the class that uses System.out.println()
by passing a PrintStream
as a collaborator and then using System.out
in production and a Test Spy in the test. That is, use Dependency Injection to eliminate the direct use of the standard output stream.
In Production
ConsoleWriter writer = new ConsoleWriter(System.out));
In the Test
ByteArrayOutputStream outSpy = new ByteArrayOutputStream();
ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy));
writer.printSomething();
assertThat(outSpy.toString(), is("expected output"));
Discussion
This way the class under test becomes testable by a simple refactoring, without having the need for indirect redirection of the standard output or obscure interception with a system rule.
You can set the System.out print stream via setOut() (and for in
and err
). Can you redirect this to a print stream that records to a string, and then inspect that ? That would appear to be the simplest mechanism.
(I would advocate, at some stage, convert the app to some logging framework - but I suspect you already are aware of this!)
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:
- I developed this library since I could not find any suitable solution for my own needs.
- Only bindings for
log4j
,log4j2
andlogback
are available at the moment, but I am happy to add more.