Skip to content
Spring Boot sb testing 3 min read

Mockito

Mockito lets you replace a class’s collaborators with controllable test doubles, so you can test one class in complete isolation — no Spring context, no database, no network. It is bundled with spring-boot-starter-test and is the foundation of fast unit tests.

Why mock collaborators

A service usually depends on a repository, a mail client, or another service. In a unit test you do not want to hit a real database or send real email — you want to control exactly what those collaborators return and then assert how your class reacts. A mock is an object that records calls and returns whatever you tell it to.

Creating mocks

You can create a mock programmatically with Mockito.mock(...), but in practice you use @Mock plus @InjectMocks and let the MockitoExtension wire them.

import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    OrderRepository repository;          // a fake repository

    @Mock
    EmailClient emailClient;             // a fake mail client

    @InjectMocks
    OrderService service;                // mocks injected into this
}

@ExtendWith(MockitoExtension.class) initializes the @Mock fields and constructs @InjectMocks with them. No Spring context starts, so the test is fast.

Stubbing with when/thenReturn

By default a mock returns “empty” values — null, 0, an empty Optional, an empty list. Use when(...).thenReturn(...) to define behavior.

when(repository.findById(1L))
        .thenReturn(Optional.of(new Order(1L, "PAID")));

when(repository.count()).thenReturn(42L);

// Stub a thrown exception
when(repository.findById(999L))
        .thenThrow(new EntityNotFoundException("not found"));

For void methods use doThrow(...).when(mock).method().

A complete pure unit test

Here is the service under test and a full test that stubs its repository and verifies a side effect.

@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository repository;
    private final EmailClient emailClient;

    public Order place(String customer) {
        Order order = repository.save(new Order(customer, "NEW"));
        emailClient.sendConfirmation(customer, order.getId());
        return order;
    }
}
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock OrderRepository repository;
    @Mock EmailClient emailClient;
    @InjectMocks OrderService service;

    @Test
    void placeSavesOrderAndSendsEmail() {
        var saved = new Order(7L, "alice", "NEW");
        when(repository.save(any(Order.class))).thenReturn(saved);

        Order result = service.place("alice");

        assertThat(result.getId()).isEqualTo(7L);
        verify(emailClient).sendConfirmation("alice", 7L);
    }
}

Output:

OrderServiceTest > placeSavesOrderAndSendsEmail() PASSED
BUILD SUCCESS

Verifying interactions

verify(...) asserts that a method was (or was not) called, and how many times.

verify(emailClient).sendConfirmation("alice", 7L); // exactly once (default)
verify(repository, times(1)).save(any());
verify(emailClient, never()).sendConfirmation(eq("bob"), anyLong());
verify(repository, atLeastOnce()).count();
verifyNoMoreInteractions(emailClient);

To inspect the exact argument passed, capture it with @Captor / ArgumentCaptor.

@Captor ArgumentCaptor<Order> orderCaptor;

verify(repository).save(orderCaptor.capture());
assertThat(orderCaptor.getValue().getStatus()).isEqualTo("NEW");

Argument matchers

When you stub or verify with a matcher like any(), all arguments must be matchers. Mix raw values in via eq(...).

MatcherMatches
any() / any(Type.class)Any value of that type
eq(value)A specific value (use alongside other matchers)
anyLong(), anyString()Any primitive/string
argThat(predicate)A custom condition
isNull() / isNotNull()Null checks
// Correct: every argument is a matcher
when(repository.search(eq("alice"), anyInt())).thenReturn(List.of());

Spies — partial mocks

A spy wraps a real object: real methods run unless you stub them. Use sparingly, for legacy code or when you need most behavior intact.

List<String> list = spy(new ArrayList<>());
list.add("a");                       // real method runs
when(list.size()).thenReturn(100);   // stub one method
assertThat(list.size()).isEqualTo(100);
assertThat(list.get(0)).isEqualTo("a");

Warning: When stubbing a spy, prefer doReturn(...).when(spy).method() over when(spy.method())... — the latter actually calls the real method while setting up the stub, which can have side effects.

Tip: If you find yourself building a huge web of mocks for one test, the class under test is probably doing too much. That pressure is a useful design signal — split the responsibilities.

Last updated June 13, 2026
Was this helpful?