Skip to content

Mockito: Different Ways of Verification

The Security Audit Log Analogy

Imagine a high security bank vault. At the end of every business day, the chief security auditor does not merely inspect whether the vault door is currently locked. The auditor reviews the digital audit log to verify every single interaction that occurred: Was the door opened exactly twice? Was the alarm disabled before opening? Did anyone attempt to access the vault after hours? Were any unauthorized keycards presented?

In unit testing, this process is called verification. Assertions check the final state or return values of your methods. But for methods that return void, interact with external message brokers, send notifications, or invoke payment gateways, verifying the output is impossible because there is no return value. Instead, you verify the interactions: which methods were invoked on your mock objects, with what parameters, how many times, and in what exact sequence.

This lecture covers all verification techniques in Mockito, including invocation counts (times, never, atLeast, atMost), verifying execution order with InOrder, verifying asynchronous interactions with timeouts, and checking for unexpected calls.


Verification vs Assertion: The Core Distinction

Understanding the difference between assertions and verification is fundamental to writing clean tests.

DimensionAssertion (Assertions.*)Verification (Mockito.verify())
FocusOutput accuracy and final stateCollaborator interactions and behavior
Question Answered"Did the method calculate the right value?""Did the method invoke its dependencies properly?"
Common TargetMethods returning values (primitives, domain objects)Methods returning void (logging, sending emails, publishing events)
ToolingJUnit 5 Assertions.assertEqualsMockito verify(mock).someMethod()
Failure MeaningLogic produced incorrect dataCollaborator was called with wrong args or wrong number of times
java
// Assertion: tests output
assertEquals("ACTIVE", user.getStatus());

// Verification: tests side effect / interaction
verify(emailService, times(1)).sendWelcomeEmail("user@example.com");

Basic Verification Syntax

The foundational method is Mockito.verify():

java
import static org.mockito.Mockito.*;

// Verifies that sendNotification was called exactly once with argument "Hello"
verify(notificationService).sendNotification("Hello");

By default, calling verify(mock).method() without specifying a verification mode is equivalent to verify(mock, times(1)).method(). If the method was never called, or if it was called multiple times, Mockito throws an ArgumentsAreDifferent or TooManyActualInvocations error with a clear failure report.


Verification Modes: Controlling Invocation Counts

Mockito provides an array of verification modes via org.mockito.Mockito.*:

1. times(int desiredNumberOfInvocations)

Verifies that the method was called an exact number of times:

java
// Called exactly three times
verify(paymentGateway, times(3)).charge(anyDouble());

2. never()

Verifies that the method was never called with matching arguments:

java
// Should never execute if balance was insufficient
verify(paymentGateway, never()).charge(anyDouble());
verify(notificationService, times(0)).sendReceipt(anyString()); // equivalent

3. atLeastOnce() and atLeast(int minNumberOfInvocations)

Verifies that the method was invoked at least once, or at least a specified number of times:

java
// Must be called at least once
verify(auditLogger, atLeastOnce()).logEvent(any());

// Must be called at least twice
verify(retryHandler, atLeast(2)).attemptReconnect();

4. atMostOnce() and atMost(int maxNumberOfInvocations)

Verifies that the method was called no more than a specified ceiling:

java
// Must not exceed one call
verify(cacheService, atMostOnce()).evictKey("user-101");

// Must not exceed three calls
verify(rateLimiter, atMost(3)).acquireToken();

5. only()

Verifies that this was the only method invoked on the mock, and that it was invoked exactly once:

java
// Verifies notifyCustomer was called once, and NO OTHER method on customerService was touched
verify(customerService, only()).notifyCustomer("Cust-44");

Verifying Interaction Order: InOrder

In financial and transactional logic, the sequence of operations is just as critical as the operations themselves. For instance, you must authenticate a user before deducting funds, or you must reserve inventory before processing a card charge.

Mockito provides inOrder() to verify chronological sequence across one or multiple mocks:

java
import org.mockito.InOrder;
import static org.mockito.Mockito.*;

@Test
void checkout_shouldFollowStrictSequence() {
    InventoryService inventory = mock(InventoryService.class);
    PaymentService payment = mock(PaymentService.class);
    ShippingService shipping = mock(ShippingService.class);

    OrderController controller = new OrderController(inventory, payment, shipping);
    controller.checkout("order-101");

    // 1. Create InOrder verifier listing all participating mocks
    InOrder inOrder = inOrder(inventory, payment, shipping);

    // 2. Verify operations in exact sequential order
    inOrder.verify(inventory).reserveStock("order-101");
    inOrder.verify(payment).processPayment("order-101");
    inOrder.verify(shipping).dispatchOrder("order-101");
}

If dispatchOrder is called before processPayment, Mockito fails the test with a VerificationInOrderFailure identifying the out of sequence invocation.


Verifying No Interactions and No More Interactions

1. verifyNoInteractions(Object... mocks)

Validates that zero methods were invoked on the provided mock objects throughout the entire test. This is essential when testing guard clauses or validation failures where execution should exit early:

java
@Test
void register_shouldAbortEarly_whenEmailIsInvalid() {
    UserRepository repository = mock(UserRepository.class);
    EmailService emailService = mock(EmailService.class);

    RegistrationService service = new RegistrationService(repository, emailService);

    // Act with invalid input
    service.register("invalid-email-format");

    // Assert that collaborators were never touched
    verifyNoInteractions(repository);
    verifyNoInteractions(emailService);
}

2. verifyNoMoreInteractions(Object... mocks)

Validates that no additional, unverified calls occurred on the mock. After you verify all expected calls, invoking verifyNoMoreInteractions guarantees that no surprise side effects took place:

java
@Test
void updateProfile_shouldOnlyTouchAllowedFields() {
    AuditService audit = mock(AuditService.class);
    UserProfileService profile = new UserProfileService(audit);

    profile.updateName("user-1", "Alice");

    // Verify the expected call
    verify(audit).logNameChange("user-1", "Alice");

    // Ensure no other audit methods were called
    verifyNoMoreInteractions(audit);
}

Asynchronous Verification with Timeouts

When testing asynchronous code — such as message listeners or background worker threads — the method under test may return immediately while the collaborator is invoked a few milliseconds later on a separate thread.

Standard verify() checks immediately and will fail if the background thread has not finished yet. Mockito provides timeout() to poll until the invocation happens or the timeout expires:

java
// Wait up to 500 milliseconds for the background worker to invoke publishEvent
verify(eventPublisher, timeout(500)).publishEvent(any(OrderCreatedEvent.class));

// Combine timeout with invocation counts
verify(metricsService, timeout(1000).times(2)).recordMetric("task_complete");

Complete Real World Example

Here is an end to end test demonstrating argument matchers, invocation count verification, and order checking:

java
package com.example.testing;

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

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class OrderProcessingWorkflowTest {

    @Mock
    private PaymentGateway paymentGateway;

    @Mock
    private EmailService emailService;

    @Mock
    private AuditLog auditLog;

    @InjectMocks
    private OrderProcessor orderProcessor;

    @Test
    void processValidOrder_shouldChargeCustomer_logAudit_andSendReceipt() {
        // Arrange
        when(paymentGateway.charge(anyString(), anyDouble())).thenReturn(true);

        Order order = new Order("ORD-99", "cust@example.com", 250.00);

        // Act
        orderProcessor.process(order);

        // 1. Verify exact counts
        verify(paymentGateway, times(1)).charge("ORD-99", 250.00);
        verify(emailService, times(1)).sendReceipt("cust@example.com", 250.00);

        // 2. Verify chronological order
        InOrder inOrder = inOrder(paymentGateway, auditLog, emailService);
        inOrder.verify(paymentGateway).charge("ORD-99", 250.00);
        inOrder.verify(auditLog).recordSuccess("ORD-99");
        inOrder.verify(emailService).sendReceipt("cust@example.com", 250.00);

        // 3. Verify no unexpected calls on audit log
        verifyNoMoreInteractions(auditLog);
    }

    @Test
    void processFailedOrder_shouldNeverSendReceipt() {
        // Arrange
        when(paymentGateway.charge(anyString(), anyDouble())).thenReturn(false);

        Order order = new Order("ORD-100", "cust@example.com", 50.00);

        // Act
        orderProcessor.process(order);

        // Verify payment was attempted, but email receipt was NEVER sent
        verify(paymentGateway, times(1)).charge("ORD-100", 50.00);
        verify(emailService, never()).sendReceipt(anyString(), anyDouble());
        verify(auditLog, times(1)).recordFailure("ORD-100");
    }
}

Interview Questions & Pitfalls

Q1: What is the default verification mode when no mode is specified in verify(mock).method()?

The default mode is times(1). Writing verify(mock).sendEmail() is identical to writing verify(mock, times(1)).sendEmail().

Q2: What is the difference between verifyNoInteractions and verifyNoMoreInteractions?

verifyNoInteractions(mock) asserts that zero methods were invoked on the mock throughout the entire test execution. verifyNoMoreInteractions(mock) asserts that no additional, unverified invocations occurred beyond those already validated by previous verify() calls.

Q3: When should you test with verification instead of assertions?

Use assertions when testing state and return values from methods that compute data. Use verification when testing methods that return void or produce external side effects — such as sending messages, publishing events, updating caches, or executing database mutations through repository interfaces.

Q4: How does inOrder verification work across multiple distinct mocks?

Pass all participating mocks into the Mockito.inOrder(mockA, mockB, mockC) factory method. The returned InOrder instance verifies that invocations across all provided mocks occurred in the strict chronological sequence in which you invoke inOrder.verify().

Q5: What happens if verifyNoMoreInteractions() is called on a mock that had stubbed calls?

In Mockito, stubbed invocations (e.g. when(mock.getId()).thenReturn(1)) are recorded as interactions. If the method under test invoked mock.getId(), but your test never explicitly called verify(mock).getId(), a subsequent verifyNoMoreInteractions(mock) will fail because an unverified interaction occurred.