Home/Learn/Java A–Z/Mockito and Test Doubles

Mockito and Test Doubles

Intermediate
Testing and Quality

Mockito is the standard Java mocking framework — create mocks, stub behaviour, and verify interactions to isolate units under test.

Overview

Mockito lets you create test doubles (mocks, stubs, spies) to isolate the unit under test from its dependencies. Mock objects record calls and can verify interactions. Stubs return configured values. Spies wrap real objects and selectively stub methods. Combined with JUnit 5 and AssertJ, Mockito forms the standard Java unit testing stack.

Creating Mocks and Stubbing

Mockito.mock() creates a mock. when().thenReturn() stubs a method call. @Mock and @InjectMocks annotations with MockitoExtension reduce boilerplate.

By default, unstubbed methods return safe defaults: 0 for numbers, false for booleans, empty collections, null for objects.

MockitoBasics.java
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    UserRepository repo;        // auto-created mock

    @Mock
    EmailService emailService;

    @InjectMocks
    UserService service;        // repo + emailService injected automatically

    @Test
    void findByIdReturnsUser() {
        // Stub: when repo.findById(42) is called, return this user
        User alice = new User(42, "Alice", "alice@example.com");
        when(repo.findById(42)).thenReturn(Optional.of(alice));

        User result = service.findById(42);

        assertThat(result.getName()).isEqualTo("Alice");
    }

    @Test
    void findByIdThrowsWhenNotFound() {
        when(repo.findById(anyInt())).thenReturn(Optional.empty());

        assertThatThrownBy(() -> service.findById(99))
            .isInstanceOf(UserNotFoundException.class);
    }
}

Verifying Interactions

verify() asserts that a method was called with specific arguments. Use times(), never(), atLeastOnce(), atMost() to check call counts.

ArgumentCaptor captures arguments passed to a mock for inspection — useful when the argument is constructed inside the method under test.

Verification.java
@Test
void placeOrderSendsConfirmationEmail() {
    User user = new User(1, "Alice", "alice@example.com");
    when(repo.findById(1)).thenReturn(Optional.of(user));

    service.placeOrder(1, "item-42", 2);

    // Verify email was sent exactly once
    verify(emailService, times(1))
        .sendConfirmation(eq("alice@example.com"), anyString());

    // Verify repository was never asked to delete anything
    verify(repo, never()).delete(any());
}

@Test
void captureOrderSentToRepo() {
    // Capture the Order object saved to the repo
    ArgumentCaptor<Order> captor = ArgumentCaptor.forClass(Order.class);

    service.placeOrder(1, "item-42", 2);

    verify(repo).save(captor.capture());
    Order saved = captor.getValue();

    assertThat(saved.getItemId()).isEqualTo("item-42");
    assertThat(saved.getQuantity()).isEqualTo(2);
    assertThat(saved.getStatus()).isEqualTo(OrderStatus.PENDING);
}

Spies, Answers, and Common Pitfalls

A spy wraps a real object — real methods are called by default, but you can stub specific ones. Use @Spy for partial mocking when most real behaviour is wanted.

Common pitfalls: calling when() on a void method (use doReturn/doThrow instead), stubbing methods that are not called (UnnecessaryStubbingException in strict mode), and mocking final classes (requires mockito-inline).

SpiesAndAnswers.java
// Spy — real object with selective stubbing
@Spy
List<String> spyList = new ArrayList<>();

@Test
void spyExample() {
    spyList.add("one");
    spyList.add("two");

    // Real method called
    assertThat(spyList).hasSize(2);

    // Stub one method
    doReturn(100).when(spyList).size();
    assertThat(spyList).hasSize(100); // stubbed
}

// Stubbing void methods
doNothing().when(emailService).sendConfirmation(anyString(), anyString());
doThrow(new RuntimeException("SMTP down"))
    .when(emailService).sendConfirmation(eq("bad@email"), anyString());

// Returning different values on successive calls
when(repo.findById(1))
    .thenReturn(Optional.of(user))   // first call
    .thenReturn(Optional.empty());   // second call

// Answer — compute return value dynamically
when(repo.save(any(Order.class)))
    .thenAnswer(inv -> {
        Order order = inv.getArgument(0);
        order.setId(UUID.randomUUID().toString());
        return order;
    });

Key Points to Remember

  • @Mock creates a mock; @InjectMocks creates the class under test and injects mocks.
  • when().thenReturn() stubs; verify() asserts interactions; ArgumentCaptor captures arguments.
  • Spies wrap real objects — real methods run by default, selective stubbing possible.
  • Use doReturn/doThrow for void methods and spies to avoid calling the real method.
  • Strict stubbing (MockitoExtension default) fails on unused stubs — keeps tests clean.

Practice Mockito and Test Doubles in the Playground

Run and modify code directly in your browser - no setup needed.

Interview Questions

Sign in to ask Aria
1

What is the difference between a mock and a spy in Mockito?

MediumAmazon
2

What is ArgumentCaptor and when do you use it?

MediumGoogle
3

Why should you use @InjectMocks instead of manually constructing the class under test?

EasyOracle
4

What is the difference between verify(mock, times(1)) and verify(mock)?

EasyMicrosoft
5

What is strict stubbing and why is it useful?

MediumNetflix

Ask Aria about Mockito and Test Doubles

Your personal AI tutor — ask anything about this concept