Mocking and Stubbing with Mockito

2 minute read

This chapter covers the full Mockito stubbing and verification API — how to control what mocks return and verify what was called.


Stubbing — Controlling Mock Behavior

when/thenReturn

when(userRepository.findById(1L)).thenReturn(Optional.of(alice));
when(userRepository.findById(999L)).thenReturn(Optional.empty());
when(userRepository.count()).thenReturn(42L);

when/thenThrow

when(paymentGateway.charge(any(), any()))
    .thenThrow(new PaymentException("Gateway timeout"));

Consecutive Returns

when(tokenService.generateToken())
    .thenReturn("token-1")
    .thenReturn("token-2")
    .thenReturn("token-3");

// First call → "token-1", second → "token-2", third+ → "token-3"

thenAnswer — Dynamic Responses

when(userRepository.save(any(User.class))).thenAnswer(invocation -> {
    User user = invocation.getArgument(0);
    user.setId(100L);  // simulate auto-generated ID
    return user;
});

doReturn/when vs when/thenReturn

// Standard — works for most cases
when(mock.method()).thenReturn(value);

// Use doReturn/when for:
// 1. Spies (to avoid calling real method during stubbing)
doReturn(value).when(spy).method();

// 2. Void methods
doNothing().when(mock).voidMethod();
doThrow(new RuntimeException()).when(mock).voidMethod();

Stubbing Void Methods

// Do nothing (default behavior, but explicit)
doNothing().when(emailService).sendEmail(any());

// Throw exception
doThrow(new MailException("SMTP down"))
    .when(emailService).sendEmail("bad@email");

// Execute custom logic
doAnswer(invocation -> {
    String email = invocation.getArgument(0);
    System.out.println("Would send email to: " + email);
    return null;  // void method
}).when(emailService).sendEmail(any());

Argument Matchers

When stubbing, you can match arguments flexibly:

// Exact value
when(repo.findById(1L)).thenReturn(Optional.of(alice));

// Any value of type
when(repo.findById(anyLong())).thenReturn(Optional.of(alice));
when(repo.save(any(User.class))).thenReturn(alice);

// Any string
when(repo.findByEmail(anyString())).thenReturn(Optional.empty());

// Null
when(repo.findByEmail(isNull())).thenThrow(new IllegalArgumentException());

// Custom matcher
when(repo.findByEmail(argThat(email -> email.endsWith("@test.com"))))
    .thenReturn(Optional.of(testUser));

Common Matchers

Matcher Matches
any() Any value (including null)
any(Class.class) Any instance of type
anyString() Any non-null String
anyInt(), anyLong() Any primitive value
anyList(), anyMap() Any collection
eq(value) Exact value (use when mixing matchers)
isNull() Null value
argThat(predicate) Custom condition

Matcher Rule: All or Nothing

// ❌ WRONG: Can't mix raw values with matchers
when(service.process("literal", anyInt())).thenReturn(result);

// ✅ CORRECT: Use eq() for literal values
when(service.process(eq("literal"), anyInt())).thenReturn(result);

Verification — What Was Called?

Basic Verification

// Was this method called?
verify(emailService).sendWelcomeEmail("alice@test.com");

// Was it called with any argument?
verify(emailService).sendWelcomeEmail(anyString());

Call Count Verification

// Exactly once (default)
verify(repo, times(1)).save(any());

// Exactly N times
verify(repo, times(3)).save(any());

// Never called
verify(repo, never()).delete(any());

// At least / at most
verify(repo, atLeastOnce()).findById(anyLong());
verify(repo, atLeast(2)).save(any());
verify(repo, atMost(5)).findAll();

Verify No More Interactions

verify(emailService).sendWelcomeEmail(any());
verifyNoMoreInteractions(emailService);
// Fails if any other method was called on emailService

Verify Zero Interactions

verifyNoInteractions(auditLogger);
// Fails if ANY method was called on auditLogger

Verification Order

InOrder inOrder = inOrder(userRepository, emailService);

inOrder.verify(userRepository).save(any(User.class));
inOrder.verify(emailService).sendWelcomeEmail(anyString());
// Ensures save was called BEFORE sendWelcomeEmail

Complete Example

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock private OrderRepository orderRepo;
    @Mock private PaymentGateway payment;
    @Mock private InventoryService inventory;
    @Mock private NotificationService notifications;

    @InjectMocks private OrderService orderService;

    @Test
    void shouldProcessOrderEndToEnd() {
        // Arrange
        Order order = new Order("ORD-1", List.of(
            new LineItem("SKU-A", 2),
            new LineItem("SKU-B", 1)
        ));

        when(inventory.checkStock("SKU-A")).thenReturn(true);
        when(inventory.checkStock("SKU-B")).thenReturn(true);
        when(payment.charge(any())).thenReturn(PaymentResult.success("TXN-123"));
        when(orderRepo.save(any())).thenAnswer(inv -> {
            Order o = inv.getArgument(0);
            o.setStatus(OrderStatus.CONFIRMED);
            return o;
        });

        // Act
        Order result = orderService.processOrder(order);

        // Assert
        assertEquals(OrderStatus.CONFIRMED, result.getStatus());

        // Verify interactions in order
        InOrder inOrder = inOrder(inventory, payment, orderRepo, notifications);
        inOrder.verify(inventory).checkStock("SKU-A");
        inOrder.verify(inventory).checkStock("SKU-B");
        inOrder.verify(payment).charge(any());
        inOrder.verify(orderRepo).save(any());
        inOrder.verify(notifications).sendOrderConfirmation("ORD-1");
    }

    @Test
    void shouldNotChargeWhenOutOfStock() {
        Order order = new Order("ORD-2", List.of(new LineItem("SKU-X", 1)));
        when(inventory.checkStock("SKU-X")).thenReturn(false);

        assertThrows(OutOfStockException.class,
            () -> orderService.processOrder(order));

        verify(payment, never()).charge(any());
        verify(orderRepo, never()).save(any());
        verify(notifications, never()).sendOrderConfirmation(any());
    }
}

Common Mistakes

Mistake Fix
Mixing raw values and matchers Use eq() for literals when other args use matchers
Verifying stubbed calls (redundant) Only verify side effects and important interactions
Over-verifying Don’t verify every call — focus on behavior
Forgetting doReturn for spies when(spy.method()) calls the real method first

Key Takeaways

  1. when/thenReturn — control what mocks return.
  2. doReturn/when — use for void methods and spies.
  3. Argument matchers — any(), eq(), argThat() for flexible matching.
  4. verify — confirm interactions happened (or didn’t).
  5. Don’t over-verify — test behavior, not implementation details.

What’s Next?

Next: ArgumentCaptor — capture and inspect the exact objects passed to mock methods.

Updated: