Skip to content
Chapter 18 · JUnit 5 & Mockito

Stubbing: Matchers, Static and Dynamic Answers ​

The architecture chapter showed how stubbing works inside Mockito. This chapter covers every way to write a stub.

Without a stub, a mock returns a default: null for objects, 0 for numbers, false for boolean, an empty collection, Optional or stream. Stubbing overrides the default.

java
@Test
void testWithoutStub() {
    Calculator calculator = Mockito.mock(Calculator.class);
    int sum = calculator.add(4, 2);
    assertEquals(6, sum);   // FAILS: a mock returns 0, it never runs the real add
}

1. when(...).thenReturn(...) ​

Return a value when the method is called with these exact arguments:

java
Calculator calculator = Mockito.mock(Calculator.class);

when(calculator.add(4, 2)).thenReturn(10);

assertEquals(10, calculator.add(4, 2));
assertEquals(0, calculator.add(5, 4));    // arguments differ: the stub does not apply, default 0

If several stubs match a call, the most recently defined one wins.

2. when(...).thenThrow(...) ​

Make the call throw, to test how the code under test handles failures.

Unchecked exceptions (subclasses of RuntimeException; the compiler does not force you to handle them) can be thrown from any method:

java
when(calculator.add(4, 2)).thenThrow(new NullPointerException());

assertThrows(NullPointerException.class, () -> calculator.add(4, 2));

Checked exceptions (the compiler forces a try/catch or a throws) can be thrown only if the stubbed method declares them:

java
public int add(int a, int b) throws IOException { ... }
java
@Test
void testCheckedException() throws IOException {     // calling add() requires handling IOException
    Calculator calculator = Mockito.mock(Calculator.class);

    when(calculator.add(4, 2)).thenThrow(new IOException());

    assertThrows(IOException.class, () -> calculator.add(4, 2));
}

Stubbing a checked exception that the method does not declare fails at once: "Checked exception is invalid for this method! Invalid: java.io.IOException". A real add could never throw it, so the test would be testing an impossible situation.

3. Consecutive stubbing ​

Chain answers to return different results on successive calls:

java
when(service.get("u123"))
        .thenReturn(null)          // 1st call
        .thenReturn("Alice");      // 2nd call, and every call after it

The last answer repeats for all further calls. Answers can mix values and exceptions:

java
when(calculator.add(4, 2))
        .thenReturn(5)
        .thenReturn(6)
        .thenThrow(new NullPointerException())
        .thenReturn(7);

assertEquals(5, calculator.add(4, 2));
assertEquals(6, calculator.add(4, 2));
assertThrows(NullPointerException.class, () -> calculator.add(4, 2));
assertEquals(7, calculator.add(4, 2));
assertEquals(7, calculator.add(4, 2));   // last value repeats

Shorthand: thenReturn(5, 6, 7).

4. doReturn(...).when(...) ​

Stubs without calling the real method while stubbing, so it is the safe choice for spies:

java
Calculator calculator = spy(new Calculator());

when(calculator.add(4, 2)).thenReturn(100);   // returns 100 later, but runs the real add() now
doReturn(100).when(calculator).add(4, 2);     // runs nothing now

int sum = calculator.add(4, 2);               // 100

Why, see the architecture chapter. One trade-off: doReturn takes an Object, so the compiler does not check that the value fits the method's return type; a mismatch only fails when the test runs.

5. doNothing() for void methods ​

java
class NotificationService {
    void sendEmail(String userId) { ... }   // sends a real email
}

On a mock, a void method already does nothing; no stub is needed:

java
NotificationService mockNotify = Mockito.mock(NotificationService.class);
mockNotify.sendEmail("u1");   // does nothing

On a spy, an unstubbed call runs the real method and would send the email. doNothing() stops that:

java
NotificationService spyNotify = Mockito.spy(new NotificationService());

doNothing().when(spyNotify).sendEmail(anyString());

spyNotify.sendEmail("u1");    // does nothing

6. doThrow() for void methods ​

when(...) needs a method call that returns a value, because the call is its argument. A void call cannot be an argument:

java
when(mockNotify.sendEmail("u1")).thenThrow(new RuntimeException());   // does not compile

Use doThrow instead:

java
@Test
void testDoThrow() {
    NotificationService mockNotify = Mockito.mock(NotificationService.class);

    doThrow(new RuntimeException()).when(mockNotify).sendEmail("u1");

    assertThrows(RuntimeException.class, () -> mockNotify.sendEmail("u1"));
}

The do... family (doReturn, doThrow, doNothing, doAnswer) works for every method, void or not, mock or spy. when(...) is more readable and type-checked, so it is the usual choice for non-void methods on mocks.

7. Argument matchers ​

Exact arguments are often too strict: when(calculator.add(4, 2)) does not apply to add(5, 4). Matchers match arguments flexibly:

java
when(calculator.add(anyInt(), anyInt())).thenReturn(10);

assertEquals(10, calculator.add(4, 2));
assertEquals(10, calculator.add(5, 4));

Most matchers are static methods of ArgumentMatchers (also reachable through Mockito); the comparison ones are in AdditionalMatchers.

MatcherMatchesExample
any()anything, including nullwhen(service.process(any())).thenReturn("ok")
any(User.class)any User, not nullwhen(repo.save(any(User.class))).thenReturn(user)
anyString()any String, not nullwhen(service.getUser(anyString())).thenReturn(user)
anyInt(), anyLong(), anyDouble(), anyBoolean()any value of that typewhen(repo.findById(anyLong())).thenReturn(entity)
anyList(), anyMap(), anySet()any non-null collection of that typewhen(service.process(anyList())).thenReturn(true)
eq(value)exactly this valuewhen(calculator.add(anyInt(), eq(5))).thenReturn(15)
isNull(), notNull()null / anything but nullwhen(service.process(isNull())).thenReturn("empty")
gt(n), lt(n), geq(n), leq(n)greater / less than (or equal to) nwhen(service.calculate(gt(100))).thenReturn("high")
contains(s), startsWith(s), endsWith(s)a string containing / starting / ending with swhen(service.getUser(startsWith("user_"))).thenReturn(user)
matches(regex)a string matching the regexwhen(service.getUser(matches("user\\d+"))).thenReturn(user)

Since Mockito 2, the typed any... matchers do not match null: anyString() and anyList() reject null; only any() accepts it.

The matcher rule: if one argument uses a matcher, all must. Mixing a matcher with a raw value fails:

java
when(calculator.add(anyInt(), 5)).thenReturn(15);         // WRONG
text
Invalid use of argument matchers!
2 matchers expected, 1 recorded

Wrap the fixed value in eq():

java
when(calculator.add(anyInt(), eq(5))).thenReturn(15);     // correct

8. Dynamic stubbing: thenAnswer() and doAnswer() ​

All stubs so far are static: a fixed value per call. An answer is a lambda that runs on each call, with access to the call's arguments, and computes the result.

The class under test:

java
public class UserService {

    private final UserRepository repository;

    public UserService(UserRepository repository) {
        this.repository = repository;
    }

    public void register(User user) {
        repository.save(user);
    }

    public User getUser(Long id) {
        return repository.findById(id);
    }
}

With static stubbing:

java
@Test
void testWithoutDynamicStubbing() {
    UserRepository repository = mock(UserRepository.class);
    UserService userService = new UserService(repository);

    User user1 = new User(1L, "A");
    User user2 = new User(2L, "B");

    userService.register(user1);   // save() on a mock does nothing anyway
    userService.register(user2);

    when(repository.findById(1L)).thenReturn(user1);
    when(repository.findById(2L)).thenReturn(user2);

    assertEquals("A", userService.getUser(1L).getName());
    assertEquals("B", userService.getUser(2L).getName());
}

It passes, but it is weak:

  1. Every new user needs its own stub: 10 users, 10 more stubs.
  2. It does not test the real behavior: what is saved can be found again. save stores nothing, and findById returns whatever we wired up by hand; the test would pass even if register never called save.

With dynamic stubbing, the mock behaves like a tiny in-memory repository:

java
@Test
void testWithDynamicStubbing() {
    UserRepository repository = mock(UserRepository.class);
    UserService userService = new UserService(repository);

    Map<Long, User> store = new HashMap<>();

    // save: put the argument in the map (save is void, so doAnswer)
    doAnswer(invocation -> {
        User user = invocation.getArgument(0);
        store.put(user.getId(), user);
        return null;                        // a void method's answer returns null
    }).when(repository).save(any());

    // findById: read from the map
    when(repository.findById(anyLong())).thenAnswer(invocation -> {
        Long id = invocation.getArgument(0);
        return store.get(id);
    });

    userService.register(new User(1L, "A"));
    userService.register(new User(2L, "B"));

    assertEquals("A", userService.getUser(1L).getName());
    assertEquals("B", userService.getUser(2L).getName());
}

invocation.getArgument(i) returns the call's i-th argument (0-based). Now, if register stopped calling save, getUser would find nothing and the test would fail.

When to use which:

  • Stateless dependency (the output depends only on the input): static stubbing is enough.
  • Stateful dependency (the output depends on earlier calls): dynamic stubbing. Examples: a cache (get depends on put), a repository (find depends on save), retry logic (fail on the first calls, succeed later; consecutive stubbing also covers simple cases).

Strict stubs: unused stubs fail the test ​

With MockitoExtension, Mockito runs in strict stubs mode. A stub that the test never uses fails the test after it runs:

java
@ExtendWith(MockitoExtension.class)
class CalculatorTest {

    @Mock
    Calculator calculator;

    @Test
    void test() {
        when(calculator.add(4, 2)).thenReturn(10);
        calculator.add(5, 4);              // different arguments: the stub is never used
    }
}
text
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at CalculatorTest.test(CalculatorTest.java:10)

This keeps tests clean, and often catches a real bug: the code called the dependency with different arguments than the test expected. If a stub is deliberately not used in every test (for example, one shared by several tests), mark it lenient:

java
lenient().when(calculator.add(4, 2)).thenReturn(10);

Mocks created with Mockito.mock() outside the extension are not checked.

Next: Stubbing controls what a dependency returns. Chapter 19, the last, checks how the code under test used it: verification.

Check yourself ​

  1. What does an unstubbed mock method return for int, boolean, String and List?
  2. After when(calculator.add(4, 2)).thenReturn(10), what does add(5, 4) return?
  3. When can a stub throw a checked exception, and what happens otherwise?
  4. With thenReturn(1).thenReturn(2), what does the third call return?
  5. Why can when(...) not stub a void method, and what do you use instead?
  6. Is doNothing() needed for a void method on a mock? On a spy?
  7. What is wrong with when(calculator.add(anyInt(), 5)), and how do you fix it?
  8. Does anyString() match null? Does any()?
  9. When is dynamic stubbing better than static stubbing? Give an example.
  10. Under MockitoExtension, what happens to a stub the test never uses, and how can you allow it?
Answers
  1. 0, false, null, an empty list.
  2. 0: the arguments do not match, so the default applies.
  3. Only if the stubbed method declares it; otherwise Mockito fails with "Checked exception is invalid for this method!".
  4. 2: the last answer repeats.
  5. when takes the call's return value as its argument, and a void call has none (it does not compile). Use doThrow, doNothing or doAnswer with .when(mock).method(...).
  6. On a mock, no: void methods already do nothing. On a spy, yes, otherwise the real method runs.
  7. It mixes a matcher with a raw value ("Invalid use of argument matchers"). Use eq(5).
  8. anyString(): no (since Mockito 2). any(): yes.
  9. When the dependency is stateful and later calls depend on earlier ones, e.g. a repository where findById must return what save stored, or a cache.
  10. The test fails with UnnecessaryStubbingException ("Unnecessary stubbings detected"). Use lenient().when(...).