Appearance
Verification
Assertions (the assertions chapter) check return values: did the system produce the expected result? How it got there does not matter.
java
int sum = calculator.add(5, 5);
assertEquals(10, sum);Verification checks behavior: which methods of the dependencies were called, how many times, with which arguments, and in what order. It validates the internal flow of a method, not just its output. It is essential when a method returns nothing (void) and its whole effect is a call on a dependency.
Cheat sheet
| Group | Code | Passes when |
|---|---|---|
| Basic | verify(mock).method() | called exactly once (the default) |
verify(mock, times(n)).method() | called exactly n times | |
verify(mock, never()).method() | never called | |
| Count | verify(mock, atLeastOnce()).method() | called ≥ 1 time |
verify(mock, atLeast(n)).method() | called ≥ n times | |
verify(mock, atMostOnce()).method() | called ≤ 1 time | |
verify(mock, atMost(n)).method() | called ≤ n times | |
verify(mock, only()).method() | called once, and nothing else called on the mock | |
| Arguments | verify(mock).method(any()) | any argument |
verify(mock).method(eq(value)) | exactly this value | |
verify(mock).method(isNull()) | a null argument | |
verify(mock).method(argThat(...)) | the argument meets a condition | |
verify(mock).method(captor.capture()) | always; captures the argument for later checks | |
| Order | InOrder | calls happened in this sequence, across one or more mocks |
| No calls | verifyNoInteractions(mock) | the mock was never used |
verifyNoMoreInteractions(mock) | no calls besides those already verified | |
| Async | verify(mock, timeout(ms)).method() | the call happens within ms; returns as soon as it does |
verify(mock, after(ms)).method() | waits the full ms, then checks |
1. verify(mock).method()
java
public class UserService {
private final UserRepository repository;
public UserService(UserRepository repository) {
this.repository = repository;
}
public void register(User user) {
repository.save(user);
}
}java
@Test
void testSimpleVerify() {
// Arrange
User user = new User(1L, "John");
UserRepository repository = mock(UserRepository.class);
UserService service = new UserService(repository);
// Act
service.register(user);
// Assert: save() was called with this user
verify(repository).save(user);
}verify(repository).save(user) is the same as verify(repository, times(1)).save(user): called exactly once. Two calls would fail with "Wanted 1 time ... But was 2 times".
verify accepts only mocks and spies. As the architecture chapter showed, every call on a mock or spy is intercepted and recorded in its invocation container (method, arguments, count), and verify reads that record. A real object records nothing, so verify(new Calculator()) fails: "Argument passed to verify() is of type Calculator and is not a mock!"
2. times(n) and never()
java
Calculator calc = mock(Calculator.class);
calc.add(1, 2);
calc.add(1, 2);
calc.add(1, 2);
verify(calc, times(3)).add(1, 2); // passes: exactly 3 times
verify(calc, never()).multiply(1, 2); // passes: never called3. atLeastOnce, atLeast, atMostOnce, atMost, only
java
Calculator calc = mock(Calculator.class);
calc.add(1, 2);
calc.add(3, 4);
calc.add(5, 6);
verify(calc, atLeastOnce()).add(anyInt(), anyInt()); // 3 >= 1
verify(calc, atLeast(2)).add(anyInt(), anyInt()); // 3 >= 2
verify(calc, atMost(5)).add(anyInt(), anyInt()); // 3 <= 5java
Calculator calc = mock(Calculator.class);
calc.add(1, 2);
verify(calc, atMostOnce()).add(anyInt(), anyInt()); // 1 <= 1
verify(calc, only()).add(1, 2); // add(1, 2) once, nothing elseonly() fails if the mock had any other call: another method, or the same method a second time.
4. Argument matching
The matchers from the stubbing chapter work in verify too, with the same rule: if one argument uses a matcher, all must.
java
calc.add(5, 10);
verify(calc).add(anyInt(), anyInt()); // values do not matter
verify(calc).add(eq(5), eq(10)); // exactly 5 and 10 (same as add(5, 10))Custom conditions: argThat, intThat, ...
To check a condition on an argument, pass a lambda:
java
calc.add(5, 10);
verify(calc).add(
intThat(a -> a > 3), // first argument: any int greater than 3
eq(10) // second argument: exactly 10
);Use argThat for object arguments (argThat(user -> user.getAge() >= 18)), and the primitive versions intThat, longThat, doubleThat, ... for primitives. (argThat returns null after registering the matcher; passing null to an int parameter would throw a NullPointerException, so primitive parameters need the versions that return 0.)
ArgumentCaptor: capture, then check
Each argThat sees only its own argument. A condition across arguments, such as a + b > 50, cannot be written as a matcher (there is no argThat((a, b) -> ...)). An ArgumentCaptor captures the actual arguments so you can check them afterwards with ordinary assertions:
java
calc.add(5, 10);
ArgumentCaptor<Integer> aCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> bCaptor = ArgumentCaptor.forClass(Integer.class);
verify(calc).add(aCaptor.capture(), bCaptor.capture());
assertTrue(aCaptor.getValue() + bCaptor.getValue() > 50); // fails here: 5 + 10 = 15- One captor per argument, of that argument's type.
ArgumentCaptor<User>gives aUserfromgetValue(). capture()is a matcher, so the matcher rule applies:add(aCaptor.capture(), 10)fails; writeeq(10).- If the method was called several times,
getValue()returns the last value andgetAllValues()all of them, in order:
java
calc.add(5, 10);
calc.add(30, 40);
verify(calc, times(2)).add(aCaptor.capture(), bCaptor.capture());
aCaptor.getAllValues(); // [5, 30]
aCaptor.getValue(); // 30Captors are most useful when the code under test builds an object and passes it to a dependency: capture it, then assert on its fields. With MockitoExtension, a field @Captor ArgumentCaptor<User> userCaptor; is created for you.
5. Order: InOrder
When the sequence matters (first charge the customer, then save the order, then send the notification), use InOrder:
java
Calculator calc = mock(Calculator.class);
calc.add(1, 2);
calc.multiply(3, 4);
calc.add(5, 6);
InOrder inOrder = Mockito.inOrder(calc);
inOrder.verify(calc).add(1, 2); // first
inOrder.verify(calc).multiply(3, 4); // then
inOrder.verify(calc).add(5, 6); // thenAcross several mocks, pass them all to inOrder:
java
PaymentGateway gateway = mock(PaymentGateway.class);
OrderRepository orderRepo = mock(OrderRepository.class);
NotificationService notifier = mock(NotificationService.class);
// the workflow under test: charge -> save -> notify
gateway.charge(100);
orderRepo.save();
notifier.sendMail("order@test.com");
InOrder inOrder = Mockito.inOrder(gateway, orderRepo, notifier);
inOrder.verify(gateway).charge(100); // the customer is charged first
inOrder.verify(orderRepo).save(); // then the order is saved
inOrder.verify(notifier).sendMail("order@test.com"); // then the mail is sentHow InOrder works: each inOrder.verify must match a call that happened after the call matched by the previous one. It does not require verifying every call, nor that the first verified call was the first call made:
java
// calls were: add(1, 2), multiply(3, 4), add(5, 6)
inOrder.verify(calc).add(1, 2);
inOrder.verify(calc).add(5, 6); // passes: comes after add(1, 2); multiply is skipped
inOrder.verify(calc).add(5, 6); // passes on its ownjava
inOrder.verify(calc).add(5, 6);
inOrder.verify(calc).add(1, 2); // FAILS: add(1, 2) was not called after add(5, 6)text
Verification in order failure
Wanted but not invoked:
calculator.add(1, 2);
Wanted anywhere AFTER following interaction:
calculator.add(5, 6);6. verifyNoInteractions and verifyNoMoreInteractions
verifyNoInteractions(mock): the mock was never used at all.
java
UserRepository repository = mock(UserRepository.class);
NotificationService notifier = mock(NotificationService.class);
repository.findById(1L); // only the repository is used
verifyNoInteractions(notifier); // passes
verifyNoInteractions(repository); // would failverifyNoMoreInteractions(mock): after the calls you have verified, there were no other calls. It does not mean "no calls at all".
java
Calculator calc = mock(Calculator.class);
calc.add(1, 2);
calc.multiply(3, 4);
verify(calc).add(1, 2);
verify(calc).multiply(3, 4);
verifyNoMoreInteractions(calc); // passes: both calls were verifiedIf the code also called calc.multiply(5, 6), the last line would fail with "No interactions wanted here", listing the unverified call. Use it sparingly: it makes tests fail on any harmless extra call.
7. Asynchronous code: timeout() and after()
java
public class AsyncOrderService {
private final OrderRepository orderRepository;
private final NotificationService notificationService;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public AsyncOrderService(OrderRepository orderRepository,
NotificationService notificationService) {
this.orderRepository = orderRepository;
this.notificationService = notificationService;
}
public boolean placeOrder(String customerEmail) {
// 1. save the order, synchronously
orderRepository.save();
// 2. send the email in the background
executor.submit(() -> {
try {
Thread.sleep(100); // simulate network delay
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
notificationService.sendMail(customerEmail);
});
// 3. return without waiting for the email
return true;
}
}java
@Test
void testAsyncOrderService() {
OrderRepository orderRepo = mock(OrderRepository.class);
NotificationService emailService = mock(NotificationService.class);
AsyncOrderService service = new AsyncOrderService(orderRepo, emailService);
service.placeOrder("customer@example.com");
verify(orderRepo).save(); // passes: it ran synchronously
verify(emailService).sendMail("customer@example.com"); // FAILS: "Wanted but not invoked"
}placeOrder returns at once and the test verifies within milliseconds, while the background thread is still sleeping; sendMail has not happened yet. A plain verify checks immediately.
timeout(ms) waits up to ms, checking repeatedly, and returns as soon as the verification passes:
java
verify(emailService, timeout(500)).sendMail("customer@example.com");
// passes after about 100 ms; fails only if 500 ms pass without the callafter(ms) always waits the full ms, then verifies. That makes exact counts reliable. timeout(500) passes as soon as the first call arrives, so it cannot catch a duplicate email that is sent later. after sees every call made in the window:
java
verify(emailService, after(500).times(1)).sendMail("customer@example.com");
// waits 500 ms; fails with "Wanted 1 time ... But was 2 times" if a second mail was sentafter(ms).never() checks that something does not happen within a period. Both modes combine with the counts: timeout(500).times(2), after(500).atMost(1).
Choose timeout for "this must happen" (fast), after for "exactly this many times" or "never" (slower: always waits).
Where to go next
This completes unit testing with JUnit 5 and Mockito: each class tested in isolation, with its dependencies mocked. The next level is integration testing, for example with Spring's test support: real beans are created and wired together (a real controller calls a real service, which calls a real repository), and only the edges, such as an external API, are mocked or replaced.
Next: That completes the series. You can now write unit tests with JUnit 5, organise and select them, and isolate each class from its dependencies with Mockito, checking both what it returns and how it uses its collaborators.
Check yourself
- How does verification differ from an assertion?
- What does
verify(mock).method()check by default? - Why can
verifyonly be used with mocks and spies? - When does
verify(calc, only()).add(1, 2)fail? - Why use
intThatinstead ofargThatfor anintparameter? - How do you check a condition involving two arguments together?
- With two calls, what do a captor's
getValue()andgetAllValues()return? - Calls were
add(1, 2),multiply(3, 4),add(5, 6). DoesinOrder.verify(calc).add(1, 2); inOrder.verify(calc).add(5, 6);pass? And verifyingadd(5, 6)thenadd(1, 2)? - What is the difference between
verifyNoInteractionsandverifyNoMoreInteractions? - Why does a plain
verifyfail for a call made on a background thread, and how dotimeoutandafterdiffer?
Answers
- An assertion checks the output; verification checks behavior: which dependency methods were called, how often, with which arguments and in which order.
- That the method was called exactly once with those arguments:
times(1). - Only mocks and spies record their calls (in the invocation container), and
verifyreads that record; a real object records nothing. - If
add(1, 2)was not called exactly once, or if any other call was made on the mock. argThatreturnsnull, which cannot be passed to anintparameter (NullPointerException);intThatreturns0.- Capture the arguments with
ArgumentCaptors inverify, then assert on the captured values. getValue()returns the last value;getAllValues()returns all of them in call order.- Yes: each verified call comes after the previous one, and skipped calls do not matter. No:
add(1, 2)did not happen afteradd(5, 6). verifyNoInteractions: the mock was never used.verifyNoMoreInteractions: no calls other than those already verified.- The test verifies before the background thread makes the call.
timeout(ms)polls and returns as soon as the verification passes;after(ms)always waits the full time, so it can check exact counts ornever().