Explain Codes LogoExplain Codes Logo

Junit test for System.out.println()

java
test-engineering
logging
dependency-injection
Nikita BarsukovbyNikita BarsukovยทNov 12, 2024
โšกTLDR

Quickly validate System.out.println() output in JUnit by redirecting System.out to a ByteArrayOutputStream. Check the content with JUnit assertions. Let's dive into it:

import org.junit.Assert; import org.junit.Before; import org.junit.After; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.PrintStream; public class HelloWorldPrinterTest { private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; // This part is like setting the table before dinner ๐Ÿ˜‰ @Before public void setUpStreams() { System.setOut(new PrintStream(outContent)); } // Time to clean up! We had fun, didn't we? ๐Ÿ˜œ @After public void restoreStreams() { System.setOut(originalOut); } @Test public void testPrintln() { System.out.println("Hello, World!"); Assert.assertEquals("Hello, World!\n", outContent.toString()); } }

Grab the printed content with outContent.toString() and assert its correctness with Assert.assertEquals.

Extending the fast answer

Checking error output

You can test System.err output in the same way you did it for System.out. Just redirect System.err to a new ByteArrayOutputStream:

private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); private final PrintStream originalErr = System.err; @Before public void setUpErrorStreams() { System.setErr(new PrintStream(errContent)); } @After public void restoreErrorStreams() { System.setErr(originalErr); } @Test public void testErrorOutput() { System.err.println("Oops!"); Assert.assertEquals("Oops!\n", errContent.toString()); }

Just like we said ๐Ÿ˜ƒ, what System.out can do, System.err can do too!

Testing System.exit() calls

If your application ends execution using System.exit(), you can test this by using the ExpectedSystemExit rule in the System Rules library:

import org.junit.Rule; import org.junit.contrib.java.lang.system.ExpectedSystemExit; public class ApocalypseNowTest { @Rule public final ExpectedSystemExit exit = ExpectedSystemExit.none(); @Test public void testEndOfTheWorld() { exit.expectSystemExitWithStatus(-1); // Code that triggers the end of times (I mean, the call to System.exit(-1)) } }

Brace yourself, Armageddon is coming! ๐Ÿ˜Ž

Making testing easier with Dependency Injection

You can inject a PrintStream for System.out into classes that write to the console, making mocking or replacement easier for testability:

public class Command { private PrintStream out; public Command(PrintStream out) { this.out = out != null ? out : System.out; } public void execute() { out.println("Executing command..."); } }

And then test it like this:

@Test public void testExecute() { ByteArrayOutputStream outContent = new ByteArrayOutputStream(); Command command = new Command(new PrintStream(outContent)); command.execute(); Assert.assertEquals("Executing command...\n", outContent.toString()); }

It's like giving your classes their own personal megaphone. ๐Ÿ“ข

More testing tips: Logging

Redirecting your output to a logging framework like Log4j or SLF4J can open doors to more elegant, granular testing:

Logger logger = LogManager.getLogger(MyClass.class); ByteArrayOutputStream logContent = new ByteArrayOutputStream(); Appender mockAppender = createAppender("mockAppender", logContent); // Show-off time for Appender logger.addAppender(mockAppender); // Trigger logging in your application // Assert log content String logOutput = logContent.toString(); Assert.assertTrue("There's a glitch in the matrix, the message is lost! ๐Ÿ˜ฑ", logOutput.contains("Expected message"));

Or, you can assert over the log messages with the help of ExpectedLogs

import org.apache.commons.testing.logging.ExpectedLogs; import org.apache.logging.log4j.Level; import org.junit.Rule; public class LogMessageTest { @Rule public ExpectedLogs logs = ExpectedLogs.none(MyClass.class); @Test public void checksLogMessage() { logs.expect(Level.ERROR).toHaveMessageContaining("Something bad happened! ๐Ÿง"); // Code that triggers the error log } }

Hear that? That's the sweet sound of logs being tested. ๐ŸŽถ