Mockito's @After and verifyNoMoreInteractions

Viewed 2729

I want to make sure every test verifies all the interactions with its mocks, so I just added a method annotated with @After with a verifyNoMoreInteractions with all the mocks as arguments.

@After
public void after(){
  verifyNoMoreInteractions(mock1,mock2,mock3,...)
}

It works, but if an interaction occurs where none was expected, how can you know which test is the problematic one?

3 Answers

@After is executed right after each test of this class finished execution. Regardless if the test failed or not. Then if errors happen in the @After method it could cover/hide the errors happened in the test method...

Note that @AfterClass Is executed after all tests of this class finished execution.

Generally speaking I found it is a good style to have a behavioural Test split in three parts- call it A/A/A or call it Given/When/Then

Arrange / Given part: Setup up objects and behaviour

Act or When: Execute implementation

Assert or Then: Verify behaviour and results (Hint: this is where the verifyNoMoreInteractions goes)

You're misusing method verifyNoMoreInteractions(). It should be called inside each test you want to verify.

From the other hand @After is supposed to be used for cleaning/closing the resourses used by your test methods.

What you need is a custom TestWatcher rule. See below an example of such rule:

public class VerifyNoMoreInteractionsRule extends TestWatcher {

    private final List<Object> mocks = new ArrayList<>();

    public void add(Object mock){
        mocks.add(mock);
    }

    @Override
    protected void succeeded(Description description) {
        verifyNoMoreInteractions(mocks.toArray());
    }

}

Then you can use it in your unit tests:

@RunWith(MockitoJUnitRunner.class)
public class VerifyTest {

    @Rule
    public VerifyNoMoreInteractionsRule noMoreInteractionsRule = new VerifyNoMoreInteractionsRule();

    @Mock
    private YourMock yourMock;

    @Mock
    private AnotherMock anotherMock;

    @Before
    public void setUp(){
        // Register the mocks you want to verify after each test
        noMoreInteractionsRule.add(yourMock);
        noMoreInteractionsRule.add(anotherMock);
    }

    @Test
    public void test(){
        // Put your ordinary test code here
    }

}

The rule will be applied to each test.

For JUnit5 it can be done using AfterEachMethodAdapter where you check whether the test has thrown an exception. If yes, no mocks are verified:

public class MockAwareTest {

    @RegisterExtension
    public MockVerifier mockVerifier = new MockVerifier();


    @BeforeEach
    void setUp() {
        mockVerifier.addMocks(...);
    }

}

And the MockVerifier:

public class MockVerifier implements AfterEachMethodAdapter {

    private final List<Object> mocks = new LinkedList<>();

    public void addMocks(final Object... mock) {
        mocks.addAll(asList(mock));
    }

    @Override
    public void invokeAfterEachMethod(
            final ExtensionContext context,
            final ExtensionRegistry registry) {
        // if the test didn't fail, check the mocks
        if (context.getExecutionException().isEmpty()) {
            verifyNoMoreInteractions(mocks.toArray());
        }
    }

}
Related