Labs ICT
โญ Pro Login

Mock Verification and Stubbing

Now that you know how to create mocks and stub methods, let us dig into verification and more advanced stubbing patterns. These are the tools that make your tests truly powerful and precise.

Verifying Interactions


import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;

class VerificationTest {

    @Test
    void verifyMethodCalled() {
        List<String> mockList = mock(List.class);
        mockList.add("hello");
        mockList.add("world");

        // Verify add was called twice
        verify(mockList, times(2)).add(anyString());

        // Verify add was called once with specific arg
        verify(mockList).add("hello");

        // Verify add was never called with "foo"
        verify(mockList, never()).add("foo");

        // Verify no other interactions
        verifyNoMoreInteractions(mockList);
    }
}
    

Verification Times


verify(mock).method();          // exactly once (default)
verify(mock, never()).method(); // never
verify(mock, times(3)).method(); // exactly 3 times
verify(mock, atLeast(1)).method(); // at least once
verify(mock, atMost(5)).method(); // at most 5 times
verify(mock, atLeastOnce()).method(); // at least once
verify(mock, exactly(2)).method(); // exactly 2 times
    

Argument Matchers


import static org.mockito.ArgumentMatchers.*;

class ArgumentMatcherDemo {

    @Test
    void argumentMatching() {
        when(mock.get(anyString())).thenReturn("value");
        when(mock.get(eq("exact"))).thenReturn("exact value");

        assertEquals("exact value", mock.get("exact"));
        assertEquals("value", mock.get("anything"));

        // Useful matchers:
        // anyString()       - any string
        // anyInt()          - any int
        // any(Class)        - any of that type
        // eq(value)         - exact value
        // contains("sub")   - string contains
        // startsWith("pre") - string starts with
        // endsWith("suf")   - string ends with
        // matches("regex")  - regex match
        // argThat(arg -> arg > 5) - custom matcher
    }
}
    

Advanced Stubbing


class AdvancedStubbingTest {

    @Test
    void multipleReturnValues() {
        when(mock.iterator())
            .thenReturn(Iterator.of("a", "b"))
            .thenThrow(new RuntimeException())
            .thenReturn(Iterator.of("c"));

        assertEquals("a", mock.iterator().next());
        assertEquals("b", mock.iterator().next());
        assertThrows(RuntimeException.class, () -> mock.iterator());
        assertEquals("c", mock.iterator().next());
    }

    @Test
    void doReturnWhen() {
        // Use doReturn when you want to avoid calling the real method
        doReturn("mocked").when(mock).toString();

        // Use doThrow for void methods
        doThrow(new RuntimeException("error"))
            .when(mock).someVoidMethod();

        // Use doNothing for void methods
        doNothing().when(mock).anotherVoidMethod();
    }

    @Test
    void thenAnswer() {
        when(mock.get(anyString()))
            .thenAnswer(invocation -> {
                String arg = invocation.getArgument(0);
                return "processed: " + arg;
            });

        assertEquals("processed: hello", mock.get("hello"));
    }
}
    

Verification Timeout


@Test
void asyncVerification() {
    // Start async operation
    service.startAsync();

    // Wait up to 2 seconds for verification
    verify(mock, timeout(2000).atLeastOnce()).callback();

    // Or with more options
    verify(mock, timeout(5000).times(3)).asyncMethod();
}
    

InOrder Verification


@Test
void verifyCallOrder() {
    mock.first();
    mock.second();
    mock.third();

    InOrder inOrder = inOrder(mock);
    inOrder.verify(mock).first();
    inOrder.verify(mock).second();
    inOrder.verify(mock).third();
}
    

Resetting Mocks


@Test
    void resetDemo() {
        when(mock.get("key")).thenReturn("value");
        assertEquals("value", mock.get("key"));

        reset(mock); // Clears all stubbings and interactions

        // Now mock returns default value
        assertNull(mock.get("key"));
    }
    

Complete Example


@ExtendWith(MockitoExtension.class)
class NotificationServiceTest {

    @Mock
    private UserRepository userRepository;

    @Mock
    private MessageQueue messageQueue;

    @InjectMocks
    private NotificationService service;

    @Test
    @DisplayName("Should send notification to active users")
    void shouldNotifyActiveUsers() {
        User user1 = new User("John", true);
        User user2 = new User("Jane", false);

        when(userRepository.findActiveUsers())
            .thenReturn(List.of(user1, user2));

        service.sendBulkNotification("Hello!");

        verify(userRepository).findActiveUsers();
        verify(messageQueue).enqueue(argThat(msg ->
            msg.getRecipient().equals("John")
        ));
        verify(messageQueue, never()).enqueue(argThat(msg ->
            msg.getRecipient().equals("Jane")
        ));
    }
}
    

๐Ÿงช Quick Quiz

Which method verifies that a mock was called a specific number of times?