Java Mocking with Mockito
Mock dependencies in Java tests with Mockito — mock, when/thenReturn, verify, and argument captors.
Java Mocking with Mockito
A unit test should exercise one class in isolation. But real classes lean on collaborators — a database, a payment gateway, an email sender — that are slow, unreliable, or have side effects you do not want in a test. Mockito is the most widely used Java library for replacing those collaborators with mocks: stand-in objects you program to return canned answers and then interrogate about how they were called. This chapter shows the Mockito API you will write every day, and proves the underlying idea with a plain-JDK program you can run right here.
Why mock at all
The class under test (the system under test, or SUT) usually receives its collaborators through its constructor — that is what dependency injection buys you. In a test you hand it a fake collaborator instead of the real one. A good fake does two jobs:
- Stubbing — it returns whatever value the test scenario needs (
charge(...)returnstrue, or throws), so you can drive the SUT down a specific path without a real network call. - Verification — it records every call it received, so afterwards the test can assert the SUT called it the right way, the right number of times, with the right arguments.
Mockito generates such a fake for any interface or non-final class at runtime, so you never hand-write one. But knowing what it generates makes the API obvious.
Creating mocks and stubbing returns
Mockito.mock(Type.class) produces a mock. By default every method returns a "nice" empty value — null for objects, false for booleans, 0 for numbers. You then override the methods you care about with when(...).thenReturn(...).
import static org.mockito.Mockito.*;
PaymentGateway gateway = mock(PaymentGateway.class);
// Stub: when charge is called with these args, return true.
when(gateway.charge("acct-7", 1999)).thenReturn(true);
// Stub a method to throw, to test error handling.
when(gateway.charge("acct-x", 1)).thenThrow(new GatewayException("down"));For void methods the order flips: doThrow(...).when(mock).method(). Stubs can also be loosened with argument matchers like anyString() and anyInt() so they fire for any call, not just one exact set of arguments.
Verifying interactions
After the SUT runs, verify(...) asserts how the mock was used. This is how you test side effects — an email that should have been sent, a row that should have been saved — without inspecting the real system.
verify(gateway).charge("acct-7", 1999); // called exactly once (default)
verify(gateway, times(2)).charge(anyString(), anyInt());
verify(gateway, never()).refund(anyString()); // must NOT have been called
verifyNoMoreInteractions(gateway); // nothing else happenedThe common verification modes:
| Mode | Meaning |
|---|---|
times(n) | Called exactly n times |
never() | Same as times(0) |
atLeastOnce() / atLeast(n) | Called at least once / n times |
atMost(n) | Called no more than n times |
only() | This was the only method called on the mock |
Capturing arguments
When you need to inspect what was passed — not just that a call happened — use an ArgumentCaptor. It grabs the actual argument so you can assert on its fields, which is invaluable when the SUT builds an object before passing it on.
ArgumentCaptor<Order> captor = ArgumentCaptor.forClass(Order.class);
verify(repository).save(captor.capture());
Order saved = captor.getValue();
assertEquals("acct-7", saved.account());
assertEquals(1999, saved.amountCents());@Mock, @InjectMocks, and spies
In real test classes you rarely call mock() by hand. The annotations wire everything up: @Mock declares a mock field, @InjectMocks builds the SUT and pushes the mocks into its constructor, and @ExtendWith(MockitoExtension.class) (JUnit 5) activates the processing.
@ExtendWith(MockitoExtension.class)
class CheckoutServiceTest {
@Mock PaymentGateway gateway;
@InjectMocks CheckoutService service; // gets the mock injected
@Test
void paysWhenGatewayApproves() {
when(gateway.charge("acct-7", 1999)).thenReturn(true);
assertEquals("PAID", service.checkout("acct-7", 1999));
verify(gateway).charge("acct-7", 1999);
}
}A spy (spy(realObject)) is the middle ground: it wraps a real object and runs real methods unless you stub them — handy for partial mocking of legacy code.
A worked example: a hand-built mock
Mockito itself is not on this page's classpath, so the runnable program below builds the mock by hand — a small class implementing the dependency interface that holds a stubbed return value and records every call. This is precisely the machinery Mockito generates for you at runtime, so reading it tells you exactly what when/thenReturn and verify do under the hood.
What to take from the run:
- The
MockGateway'sstubbedResult = trueis the hand-written form ofwhen(gateway.charge(...)).thenReturn(true); because the stub returnedtrue, the SUT printedresult : PAIDwithout any real payment ever happening. invocationCount == 1printingtrueis exactly whatverify(gateway).charge(...)checks — the mock counted that it was called once, which is how Mockito turns "did this interaction occur?" into a pass/fail assertion.- The
callslist capturedcharge(acct-7, 1999), the argument-capture idea behindArgumentCaptor: a mock remembers not just that it was called but with what, so the test can assert on the actual arguments. - Re-creating the mock with
stubbedResult = falsedrove the SUT down its other branch and printeddeclined result : DECLINED, showing how one fake lets you script every scenario the real collaborator could produce. - The guard clause returned
INVALIDbefore reaching the gateway, soinvocationCount == 0printedtrue— the runnable proof ofverify(gateway, never()).charge(...), asserting a dependency was deliberately not touched.
Practice
In a Mockito-based unit test, what is the purpose of a call like verify(gateway, never()).charge(anyString(), anyInt())?