Today someone asked how to verify that only your stubbed interactions occur, and no others (when using Mockito).
I have heard this asked this quite often, especially by people used to JMock where mocks are strict by default.
I’d be interested in knowing if there’s an out of the box way of doing this.
The normal way to do this is to verify all the calls you expect to happen and then verify no more interactions occur.
This often seems like unnecessary duplication with the “when” stubbing. You end up with something like
@Test
public void exampleOfRedundantVerify() throws Exception {
Duck duck = mock(Duck.class);
when(duck.quack()).thenReturn("quack");
when(duck.waddle()).thenReturn("waddle");
duck.quack();
duck.waddle();
//Why do we need to do this?
verify(duck).quack();
verify(duck).waddle();
verifyNoMoreInteractions(duck);
}
If you omit the two verify lines then the test will fail, despite us stubbing quack with “when”. It would be nice to remove this duplication.
Now, at this point someone will probably point out that often verifyNoMoreInteractions, and even multiple verifications in a test can be a sign of an inflexible test that is easy to break with minor implementation changes. However, sometimes you really do want to assert that a collaborator is only used in a specific way. You might also temporarily want the mocks to be more vocal about how they are used, in order to help diagnose why a test is failing.
So how can we make this better with Mockito? Mockito provides an Answer.
To implement Answer you simply have to implement
public T answer(InvocationOnMock invocation) throws Throwable
This is called when you invoke a method on a mock.
Answer is an interface that Mockito provides to allow you to specify the response to a method invocation on a Mock. It gives a bit more power than simply returning a value. For instance you can use it to capture method parameters passed to stubbed method calls for later assertions.
Answer is useful for solving this problem because we can specify a default Answer that a mock will use whenever a method invocation has not been stubbed. In our case we want the test to fail, so we can simply throw an Exception with some information about the unexpected invocation.
Here’s what we can enable
@Test
public void exampleStubbedAll() throws Exception {
Duck duck = strictMock(Duck.class);
when(duck.quack()).thenReturn("quack");
verifyNoUnstubbedInteractions(duck);
duck.quack();
}
@Test(expected = NotStubbedException.class)
public void exampleNotStubbedAll() throws Exception {
Duck duck = strictMock(Duck.class);
when(duck.quack()).thenReturn("quack");
verifyNoUnstubbedInteractions(duck);
duck.quack();
duck.waddle();
}
Here the first test only performs stubbed operations on the mock, so passes. The second test calls an unstubbed method and an Exception is thrown.
How is it implemented? First we create a static method to create ourselves a mock. The second argument here is the default answer, Mockito will invoke the “answer” method on this handler for every unstubbed invocation.
public static T strictMock(Class cls) {
return Mockito.mock(cls, new StrictMockHandler());
}
The answer implementation simply throws an Exception, if it is in Strict mode. We need to provide a toggle for “strictness” so that our when(mock.quack()) invocation during stubbing does not cause an Exception to be thrown. I believe this is necessary without horrible hacks like looking back up the stack trace to see where it is called (Or does Mockito maintain some global state about the stubbing context?)
public static class StrictMockHandler implements Answer {
public boolean strict = false;
public Object answer(InvocationOnMock invocation) throws Throwable {
if (strict) throw new NotStubbedException(invocation.getMethod().getName());
return null;
}
}
Finally, we provide a static method for toggling the strictness after our stubbings, this pulls out the default Answer from the mock using a utility that Mockito provides.
public static void verifyNoUnstubbedInteractions(Object mock) {
StrictMockHandler handler = ((StrictMockHandler) new MockUtil().getMockHandler(mock).getMockSettings().getDefaultAnswer());
handler.strict = true;
}
I don’t believe this can deasily work with @Mock annotation based mock creation. You can pass in a different answer by doing @Mock(answer=Answers.RETURNS_SMART_NULLS), but this is limited to the values on the Answers enum.
To do this with annotations I think you’d have to create a JUnit @Rule or Runner that handles a new annotation, maybe @StrictMock instead of @Mock
See the implementation and sample tests on github.