I think the 2017 answer should read like this:
- you can use a ByteArrayOutputStream
- you can use 3rd party libraries such as System Rules
- you improve your design to avoid the need to do unit testing that relies on fetching content from
System.out
Github shows that System Rules still seems "alive" (which is always a good sign when deciding if to use a 3rd party library) - and being around for 8 years implies that it is probably a mature product. So: when adding this external dependency is fine for you, just go for it.
But of course: the real answer still is: avoid writing such tests. There is nothing worse but hunting "bugs" because a legacy unit test fails all of a sudden - because some output message contains a space instead of a tab all for some stupid reason. Meaning: write code that emits Strings directly, and have unit tests check those.
Then have a single integration/function test that makes sure that your "plumbing" is correct, and that such "emitted" strings end up in System.out at some point.
Given the comment by the OP - which exactly underlines my point. The problem is code like this:
Scanner scanner = new Scanner(System.in);
if (scanner.next().equals("whatever")) {
System.out.println("well"));
}
which has direct dependencies to System.in and System.out. So, in order to test the above code, one has somehow manipulate System.in and System.out therefore. Now look at this code:
public class InOutExample {
private final Scanner scanner;
private final OutputStream out;
public InOutExample() {
this(new Scanner(), System.out);
}
/** unit test only */
InOutExample(Scanner scanner, OutputStream out) {
this.scanner = scanner;
this.out = out;
}
public void foo() {
if (scanner.next().equals("whatever")) {
out.println("well"));
}
}
The above allows you to fully unit test your foo()
method. Why? Because you can pass any scanner resp. out object into the example class that you want. You can either write your own stub implementation for the two objects, or you mock the objects and use the verification capabilities of your mocking framework to check that exactly the expected strings are sent to that special OutputStream out
!