Mockito: Mocking, Spying, Stubbing & Argument Captors

Mockito is the most widely used Java mocking framework. It lets you replace real collaborators with test doubles: mocks that record calls and return configured values, or spies that wrap real objects and selectively stub specific methods. This article covers the full Mockito API used in everyday testing.

Share

Contents

Dependency & Setup

Spring Boot's spring-boot-starter-test includes Mockito. Standalone projects add mockito-junit-jupiter for JUnit 5 integration.

XML
<!-- Spring Boot — Mockito already included -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

<!-- Standalone -->
<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-junit-jupiter</artifactId>
  <version>5.12.0</version>
  <scope>test</scope>
</dependency>

Enable Mockito annotations in JUnit 5 tests by adding @ExtendWith(MockitoExtension.class) to the test class.

Creating Mocks

Create mocks with the @Mock annotation (preferred) or the programmatic Mockito.mock() call. Inject mocks into the class under test using @InjectMocks.

Java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.Assertions;

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    OrderRepository orderRepo;   // Mockito creates a mock implementation

    @Mock
    EmailService emailService;

    @InjectMocks
    OrderService orderService;   // Mockito injects the mocks above via constructor/setter/field

    @Test
    void placesOrder() {
        Order saved = new Order("o1", "Widget", 2);
        Mockito.when(orderRepo.save(Mockito.any())).thenReturn(saved);

        Order result = orderService.place("Widget", 2);

        Assertions.assertEquals("o1", result.id());
    }
}
@InjectMocks tries constructor injection first, then setter injection, then field injection. Prefer constructor injection in your production classes: it makes the dependencies explicit and works reliably with @InjectMocks.

Stubbing: when/thenReturn

Use when(mock.method(args)).thenReturn(value) to define what a mock returns for a given call. Chain multiple thenReturn calls to return different values on successive invocations.

Java
@Test
void stubbingExamples() {
    UserRepository repo = Mockito.mock(UserRepository.class);

    // Always return the same value
    Mockito.when(repo.findById(1L))
           .thenReturn(Optional.of(new User(1L, "Alice")));

    // Return different values on successive calls
    Mockito.when(repo.count())
           .thenReturn(0L)   // first call
           .thenReturn(1L)   // second call
           .thenReturn(2L);  // third and subsequent calls

    // Compute a value dynamically with thenAnswer
    Mockito.when(repo.findById(Mockito.anyLong()))
           .thenAnswer(inv -> {
               long id = inv.getArgument(0);
               return Optional.of(new User(id, "User-" + id));
           });

    Assertions.assertEquals("User-42", repo.findById(42L).get().name());
}

Stubbing Exceptions & Void Methods

Use thenThrow to make a mock throw an exception. For void methods use the doThrow(...).when(mock).method() syntax.

Java
@Test
void stubbingExceptions() {
    PaymentGateway gateway = Mockito.mock(PaymentGateway.class);

    // Throw on a non-void method
    Mockito.when(gateway.charge(Mockito.anyDouble()))
           .thenThrow(new PaymentException("Declined"));

    Assertions.assertThrows(PaymentException.class, () -> gateway.charge(50.0));
}

@Test
void stubbingVoidMethods() {
    NotificationService notifier = Mockito.mock(NotificationService.class);

    // void methods need doThrow/doNothing/doAnswer
    Mockito.doThrow(new RuntimeException("SMTP down"))
           .when(notifier).sendEmail(Mockito.anyString());

    Mockito.doNothing()
           .when(notifier).sendSms(Mockito.anyString());  // explicit no-op (default for void)

    Assertions.assertThrows(RuntimeException.class, () -> notifier.sendEmail("a@b.com"));
}

Spies: Partial Mocking

A @Spy wraps a real object. Unstubbed methods execute normally; stubbed methods return the configured value. Use spies sparingly: they often indicate a design that could be improved.

Java
@ExtendWith(MockitoExtension.class)
class PricingServiceTest {

    @Spy
    PricingService pricingService = new PricingService(new TaxConfig(0.2));

    @Test
    void spyCallsRealMethodByDefault() {
        // Calls the real calculateBase() implementation
        double base = pricingService.calculateBase("Widget", 3);
        Assertions.assertTrue(base > 0);
    }

    @Test
    void spyCanStubSpecificMethod() {
        // Override just one method, rest are real
        Mockito.doReturn(100.0).when(pricingService).calculateBase(Mockito.anyString(), Mockito.anyInt());

        double total = pricingService.calculateTotal("Widget", 3);  // uses stubbed base
        Assertions.assertEquals(120.0, total);  // 100 * 1.2 tax
    }
}
Always use doReturn(...).when(spy).method() syntax when stubbing spy methods: never when(spy.method()).thenReturn(...). The latter actually calls the real method during the stub setup, which can cause side effects or NPEs.

Verifying Interactions

Use verify() to assert that a mock method was called with expected arguments, the right number of times, or in the right order.

Java
@Test
void verificationExamples() {
    UserRepository repo  = Mockito.mock(UserRepository.class);
    AuditLogger    audit = Mockito.mock(AuditLogger.class);
    UserService    svc   = new UserService(repo, audit);

    svc.deleteUser(42L);

    // Was called exactly once with this argument
    Mockito.verify(repo, Mockito.times(1)).deleteById(42L);

    // Was called at least once
    Mockito.verify(audit, Mockito.atLeastOnce()).log(Mockito.anyString());

    // Was never called
    Mockito.verify(repo, Mockito.never()).save(Mockito.any());

    // No other interactions happened on this mock
    Mockito.verifyNoMoreInteractions(repo);
}

@Test
void verifyOrder() {
    DataSource ds   = Mockito.mock(DataSource.class);
    Notifier notifier = Mockito.mock(Notifier.class);
    BatchJob job    = new BatchJob(ds, notifier);

    job.run();

    InOrder inOrder = Mockito.inOrder(ds, notifier);
    inOrder.verify(ds).connect();
    inOrder.verify(ds).process();
    inOrder.verify(notifier).send("done");
}

ArgumentCaptor

ArgumentCaptor captures the argument passed to a mock method so you can assert on it. Use it when you need to verify the exact object that was passed, not just whether the method was called.

Java
@ExtendWith(MockitoExtension.class)
class OrderServiceCaptorTest {

    @Mock OrderRepository repo;
    @Captor ArgumentCaptor<Order> orderCaptor;

    @InjectMocks OrderService orderService;

    @Test
    void capturesOrderOnSave() {
        orderService.place("Gadget", 5);

        Mockito.verify(repo).save(orderCaptor.capture());

        Order saved = orderCaptor.getValue();
        Assertions.assertEquals("Gadget", saved.product());
        Assertions.assertEquals(5,        saved.quantity());
        Assertions.assertNotNull(saved.createdAt());
    }
}

Argument Matchers

Argument matchers let stubs and verifications match arguments flexibly. When using matchers, all arguments in the same method call must be matchers: mix matchers and literal values using eq().

Java
UserRepository repo = Mockito.mock(UserRepository.class);

// Common matchers
Mockito.when(repo.findByEmail(Mockito.anyString()))
       .thenReturn(Optional.empty());

Mockito.when(repo.findByAgeGreaterThan(Mockito.intThat(age -> age >= 18)))
       .thenReturn(List.of(new User(1L, "Alice")));

// Mixing literal and matcher — use eq() for the literal
Mockito.when(repo.findByNameAndCity(Mockito.eq("Alice"), Mockito.anyString()))
       .thenReturn(Optional.of(new User(1L, "Alice")));

// Custom matcher with argThat
Mockito.verify(repo).save(Mockito.argThat(
    order -> order.quantity() > 0 && order.product() != null
));
MatcherMatches
any() / any(Class)Anything, including null
anyString(), anyInt(), …Any non-null value of that type
eq(value)Equals the given value
isNull() / isNotNull()Null / non-null
startsWith(str), contains(str)String prefix / substring
argThat(predicate)Custom predicate
intThat(predicate), longThat()…Custom predicate for primitives