Danziger
Danziger

Reputation: 21161

Unit testing logs using streams working just for the first test

I have a JUnit test class, which uses Mockito, in which I need to test if something is being logged correctly. It basically looks something like:

public class MyTest {

    private final PrintStream outDefault = System.out;
    private final PrintStream errDefault = System.err;

    private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
    private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();

    @Before
    public void setUp() {
        System.setOut(new PrintStream(outContent));
        System.setErr(new PrintStream(errContent));  
    }

    @After
    public void tearDown() {
        System.setOut(outDefault);
        System.setErr(errDefault);
    }

    @Test
    public void Test1_Condition_Expected() {
        assertTrue(errContent.toString().toLowerCase().contains("..."));
    }

    ...

    @Test
    public void TestN_Condition_Expected() {
        assertTrue(errContent.toString().toLowerCase().contains("..."));
    }
}

I have also tried to do a flush and close of the streams in the @After, but it does not seem to work neither:

@After
public void tearDown() {
    try {
        outContent.flush();
        errContent.flush();
        outContent.close();
        errContent.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

    System.setOut(outDefault);
    System.setErr(errDefault);
}

When I run the whole class, the first tests passes but the rest don't. If I run the tests one by one then all of them pass.

I have debugged the code and everything seems to work properly, but for tests other than the first one the streams are not getting the logs, so obviously they fail.

Upvotes: 0

Views: 234

Answers (2)

Stefan Birkner
Stefan Birkner

Reputation: 24520

The PrintStream is not flushed when you call errorContent.toString() because @After is called after your test and it flushes only errorContent. You have to flush the PrintStream around errorContent before calling toString(). The best way is to use PrintStream with autoFlush enabled.

@Before
public void setUp() {
    System.setOut(new PrintStream(outContent, true));
    System.setErr(new PrintStream(errContent, true));  
}

Upvotes: 1

Jeff Bowman
Jeff Bowman

Reputation: 95654

Note that the ByteArrayOutputStreams you're flushing:

outContent.flush();
errContent.flush();

...aren't the PrintStreams you're setting:

System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));

...and that PrintStream owns a BufferedWriter and can be set to auto-flush, but doesn't do so by default:

Optionally, a PrintStream can be created so as to flush automatically; this means that the flush method is automatically invoked after a byte array is written, one of the println methods is invoked, or a newline character or byte ('\n') is written.

My guess is that it's the PrintStream that needs to be flushed before each assertion against toString(), not the ByteArrayOutputStream.

Upvotes: 1

Related Questions