Mockito

Javaunit testingmockingBDDTDDJUnitTestNGverificationstubbing

Unit Testing Tool

Mockito

Overview

Mockito is the most popular mocking framework for Java used for Test-Driven Development (TDD) and Behavior-Driven Development (BDD). It enables the creation of test double objects (mock objects) in automated unit tests to validate interactions between objects. Mockito lets you write beautiful tests with a clean and simple API, making it a truly "tasty" mocking framework.

Details

Mockito was originally developed to address shortcomings of existing mocking frameworks like EasyMock and JMock. Today, it's used in over 30,000 GitHub projects and has been voted by the massive StackOverflow community as the best mocking framework for Java. The latest version as of 2024 is 5.14.2, requiring Java 11 or higher.

Key features of Mockito:

  • Simple API: Intuitive and easy-to-remember API design
  • Powerful verification: Detailed verification of method call counts, order, and arguments
  • Flexible stubbing: Freedom to set method return values and exceptions
  • Argument matchers: Flexible argument matching for verification
  • Spy functionality: Partial mocking of real objects
  • Static method mocking: Mock static method calls (using mockito-inline)
  • Constructor mocking: Mock class instantiation

Mockito 5 switches the default mockmaker to mockito-inline and requires Java 11. This enables mocking of final classes and methods by default.

Advantages and Disadvantages

Advantages

  1. Low learning curve: Intuitive API that's easy for beginners to master
  2. Rich documentation: Comprehensive official documentation and examples
  3. Excellent integration: Seamless integration with JUnit, TestNG, and Spring Boot
  4. Detailed error messages: Clear error information when tests fail
  5. High performance: Lightweight and fast test execution
  6. Active community: Large user community and continuous development
  7. BDD support: Behavior-driven development style support through BDDMockito

Disadvantages

  1. Over-mocking risk: Over-reliance on mocks may miss actual integration issues
  2. Java language only: Cannot be used with other programming languages
  3. Learning curve: Advanced features (custom answers, argument captors) require time to master
  4. Debugging difficulty: Complex mock setups can be hard to debug
  5. Implementation coupling: Mocks may become tightly coupled to implementation details

Reference Pages

Usage Examples

Basic Setup

Maven Dependencies

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.14.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>5.14.2</version>
    <scope>test</scope>
</dependency>

JUnit 5 Integration

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    
    @Mock
    private UserRepository userRepository;
    
    @Test
    void shouldCreateUser() {
        // Test implementation
    }
}

Mock Creation and Stubbing

Basic Mock Creation

// Create mock
List<String> mockedList = mock(List.class);

// Stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.size()).thenReturn(5);

// Usage
System.out.println(mockedList.get(0)); // "first"
System.out.println(mockedList.size()); // 5

Exception Stubbing

when(mockedList.get(anyInt())).thenThrow(new RuntimeException());

// void method exception
doThrow(new RuntimeException()).when(mockedList).clear();

Verification

Basic Verification

// Verify method calls
verify(mockedList).add("item");
verify(mockedList, times(1)).clear();
verify(mockedList, never()).remove("item");
verify(mockedList, atLeastOnce()).size();
verify(mockedList, atMost(3)).get(anyInt());

Order Verification

InOrder inOrder = inOrder(mockedList);
inOrder.verify(mockedList).add("first");
inOrder.verify(mockedList).add("second");

Argument Matchers

Built-in Matchers

// Any argument
when(mockedList.get(anyInt())).thenReturn("element");

// Specific type
when(userService.findUser(any(String.class))).thenReturn(user);

// Argument captor
ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
verify(mockedList).add(argument.capture());
assertEquals("captured value", argument.getValue());

Custom Matchers

when(userService.authenticate(argThat(user -> 
    user.getEmail().endsWith("@company.com")
))).thenReturn(true);

Spy (Partial Mocks)

Partial Mocking

List<String> list = new ArrayList<>();
List<String> spyList = spy(list);

// Real method is called
spyList.add("one");
spyList.add("two");

// Stubbing is also possible
when(spyList.size()).thenReturn(100);

assertEquals(100, spyList.size()); // Stubbed value
assertEquals("one", spyList.get(0)); // Real value

Custom Answers

Complex Behavior Definition

when(userService.processUser(any(User.class))).thenAnswer(invocation -> {
    User user = invocation.getArgument(0);
    return "Processed: " + user.getName();
});

// Simple answer with lambda
when(calculator.add(anyInt(), anyInt())).thenAnswer(invocation -> {
    int a = invocation.getArgument(0);
    int b = invocation.getArgument(1);
    return a + b;
});

BDD Style

Using BDDMockito

import static org.mockito.BDDMockito.*;

@Test
void shouldReturnUserWhenFound() {
    // Given
    User expectedUser = new User("John", "[email protected]");
    given(userRepository.findById(1L)).willReturn(expectedUser);
    
    // When
    User actualUser = userService.getUser(1L);
    
    // Then
    then(userRepository).should().findById(1L);
    assertThat(actualUser).isEqualTo(expectedUser);
}

Static Methods and Constructor Mocking

Static Method Mocking

@Test
void shouldMockStaticMethod() {
    try (MockedStatic<LocalDateTime> mockedStatic = mockStatic(LocalDateTime.class)) {
        LocalDateTime fixedTime = LocalDateTime.of(2024, 1, 1, 12, 0);
        mockedStatic.when(LocalDateTime::now).thenReturn(fixedTime);
        
        assertEquals(fixedTime, LocalDateTime.now());
    }
}

Constructor Mocking

@Test
void shouldMockConstruction() {
    try (MockedConstruction<File> mockedConstruction = mockConstruction(File.class)) {
        File file = new File("test.txt");
        
        verify(mockedConstruction.constructed().get(0)).exists();
    }
}

Advanced Verification

Strict Stubbing

// Strict mode (detects unnecessary stubs)
MockitoSession mockito = Mockito.mockitoSession()
    .initMocks(this)
    .strictness(Strictness.STRICT_STUBS)
    .startMocking();

// Change to lenient mode when needed
lenient().when(mockedList.get(0)).thenReturn("element");

Timeout Verification

// Verify calls within a time period
verify(mockedList, timeout(1000)).add("item");
verify(mockedList, timeout(1000).times(2)).clear();