Mockito verify: what it proves and what it doesn't
verify() is the right assertion for collaborators whose only observable behaviour is the call: senders, publishers, gateways. For anything that returns a value, assert on the result instead.
Originally written on 21 August 2017. Migrated from a WordPress blog and reformatted.
verify() is the most used and most misused method in Mockito. It asserts that a mock received a call. Used on the right collaborator, it is the only way to test certain behaviour. Used on the wrong one, it produces tests that pass while the code is broken and fail while the code is fine.
What verify asserts
verify(mailer).send(eq("customer@example.com"), any(Message.class));
verify(mailer, times(1)).send(any(), any());
verify(mailer, never()).send(eq("internal@example.com"), any());
verifyNoMoreInteractions(mailer);
Each of these is a statement about the interaction between the class under test and the mock: that a call happened, how many times, with what arguments, and that nothing else happened. Nothing is asserted about the outcome of the call, because a mock has no outcome.
class under test ──── calls ────▶ mock
│ │
│ state / return value │ recorded invocations
▼ ▼
assertThat(...) verify(...)
When it is the right assertion
The collaborator's side effect is the behaviour. Sending an email, publishing an event, calling a payment gateway, writing an audit entry. These have no return value that matters and no state you can inspect afterwards. The only observable fact is that the call was made with the right arguments, and verify is how you observe it.
@Test
public void notifiesCustomerWhenOrderShips() {
service.markShipped(orderId);
verify(mailer).send(eq("customer@example.com"),
argThat(m -> m.subject().contains("shipped")));
}
The negative case is equally important and only verify can express it:
@Test
public void doesNotNotifyWhenAlreadyShipped() {
service.markShipped(alreadyShippedId);
verify(mailer, never()).send(any(), any());
}
When it is the wrong assertion
The collaborator returns something the class under test uses. A repository, a pricing service, a validator. Here the outcome is observable: a return value, a thrown exception, or state in a real store. Asserting on the call instead of the outcome means the test cannot detect a wrong result.
// weak: proves the call, not the price
verify(pricing).priceFor("SKU-1");
// strong: proves the result
assertThat(order.getTotal()).isEqualByComparingTo("19.98");
A test that verifies repository.save(any()) passes when the saved order has the wrong total, the wrong status, or a null customer. A test that loads the order back and checks its fields does not.
verifyNoMoreInteractions
This asserts that the mock received no calls other than the ones already verified. It is occasionally right, for a collaborator whose every call has a cost, such as a rate-limited API. As a default it is harmful: it fails the test on any refactor that adds a harmless call, and the failure says nothing about behaviour.
Argument captors
When the argument itself needs inspection, capture it rather than matching it:
ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
verify(mailer).send(any(), captor.capture());
Message sent = captor.getValue();
assertThat(sent.body()).contains(orderId.toString());
assertThat(sent.recipients()).hasSize(1);
This turns a verify into an outcome assertion on the message, which is usually what was wanted.
Summary
Use verify for collaborators whose only observable behaviour is the call: senders, publishers, gateways. For everything else, assert on the result. If a test contains only verify calls, it is describing the implementation, and it will need rewriting the next time the implementation changes.