Mockito - how to verify that a mock was never invo

2019-02-05 17:18发布

问题:

I'm looking for a way to verify with Mockito, that there wasn't any interaction with a given mock during a test. It's easy to achieve that for a given method with verification mode never(), but I haven't found a solution for the complete mock yet.

What I actually want to achieve: verify in tests, that nothing get's printed to the console. The general idea with jUnit goes like that:

private PrintStream systemOut;

@Before
public void setUp() {
    // spy on System.out
    systemOut = spy(System.out);
}

@After
public void tearDown() {
    verify(systemOut, never());  // <-- that doesn't work, just shows the intention
}

A PrintStream has tons of methods and I really don't want to verify each and every one with separate verify - and the same for System.err...

So I hope, if there's an easy solution, that I can, given that I have a good test coverage, force the software engineers (and myself) to remove their (my) debug code like System.out.println("Breakpoint#1"); or e.printStacktrace(); prior to committing changes.

回答1:

Use this :

import static org.mockito.Mockito.verifyZeroInteractions;

// ...

private PrintStream backup = System.out;

@Before
public void setUp() {
    System.setOut(mock(PrintStream.class));
}

@After
public void tearDown() {
    verifyZeroInteractions(System.out);
    System.setOut(backup);
}


回答2:

verifyZeroInteractions(systemOut);

As noted in comments, this doesn't work with a spy.

For a roughly equivalent but more complete answer, see the answer by gontard to this question.



回答3:

You could try a slightly different tack:

private PrintStream stdout;

@Before public void before() {
    stdout = System.out;
    OutputStream out = new OutputStream() {
        @Override public void write(int arg0) throws IOException {
            throw new RuntimeException("Not allowed");
        }
    };
    System.setOut(new PrintStream(out));
}

@After public void after() {
    System.setOut(stdout);
}

If you preferred, you could switch the anonymous type for a mock and verify as Don Roby suggests.



回答4:

One way of solving this problem is to refactor the class that you're testing, to allow for the injection of a PrintStream that can be used for output. This will let you unit test it, without relying on the behaviour of the System class. You could use a package-private constructor for this injection, since you'll only ever use it from the corresponding test class. So it might look something like this.

public class MyClass{
    private PrintWriter systemOut;

    public MyClass(){
        this(System.out);
    }

    MyClass(PrintWriter systemOut){
        this.systemOut = systemOut;

        // ...any other initialisation processing that you need to do
    }
}

and within the class itself, use the systemOut variable instead of System.out wherever you call the latter.

Now, within the test class, make a mock PrintStream, and pass it to the package-private constructor, to get the object that you're going to test. Now you can run any actions you like from your tests, and use verify to check their effects on your mock PrintStream.