Unit Testing with JUnit 5 and Mockito

Introduction to Mockito Integration

Integrating Mockito with JUnit 5 requires the mockito-junit-jupiter library. This enables the use of JUnit 5 extensions for Mockito annotations. In Spring Boot environments (version 2.4.0 and later), the spring-boot-starter-test starter dependency includes these libraries automatically. For standalone setups, the following Maven dependencies are required:

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

Mock vs. Spy Objects

Mockito provides two primary mechanisms for creating test doubles: Mocks and Spies. A Mock is a dummy object that creates a proxy for the class, defaulting all method calls to returning neutral values (like null, 0, or false) unless explicitly stubbed. A Spy, conversely, wraps a real instance. By default, it calls the real methods of the object unless a specific behavior is stubbed. Internally, Mockito considers a Spy to be a specialized type of Mock.

Initializing Test Doubles

Using Annotations

The most concise method utilizes the @ExtendWith(MockitoExtension.class) annotation on the test class, combined with field annotations like @Mock and @Spy.

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

@ExtendWith(MockitoExtension.class)
class DataHandlerTest {

    @Mock
    private DataRepository mockRepo;

    @Spy
    private DataRepository spyRepo;

    @Test
    void verifyTypes() {
        System.out.println("Is Mock object? " + mockingDetails(mockRepo).isMock()); // true
        System.out.println("Is Spy object? " + mockingDetails(mockRepo).isSpy());   // false
        
        System.out.println("Is Spy also Mock? " + mockingDetails(spyRepo).isMock()); // true
        System.out.println("Is Spy object? " + mockingDetails(spyRepo).isSpy());     // true
    }
}

Programmatic Initialization

Test doubles can also be created explicitly within a setup method or test case using static methods.

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;

@ExtendWith(MockitoExtension.class)
class DataHandlerManualTest {

    private DataRepository manualMock;
    private DataRepository manualSpy;

    @BeforeEach
    void init() {
        manualMock = Mockito.mock(DataRepository.class);
        manualSpy = Mockito.spy(DataRepository.class);
    }

    @Test
    void verifyManualTypes() {
        System.out.println("Manual Mock Check: " + mockingDetails(manualMock).isMock());
        System.out.println("Manual Spy Check: " + mockingDetails(manualSpy).isSpy());
    }
}

Annotation-Based Manual Init

If the MockitoExtension is not used or manual control is needed over annotated fields (@Mock, @Spy), MockitoAnnotations.openMocks(this) can be called in a lifecycle method.

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockAnnotations;
import org.mockito.Spy;

public class AnnotationInitTest {
    @Spy
    private DataRepository repository;

    @BeforeEach
    void setUp() {
        // Initializes fields annotated with @Mock, @Spy, etc.
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void testSpy() {
        // Test logic here
    }
}

Argument Matchers

Mockito allows flexible argument matching using ArgumentMatchers. This is useful when the exact argument value is not important for the verification or stubbing logic. The any() method matches any type or value, while any(Class) restricts the match to a specific type.

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;

@Test
void argumentMatchingExample() {
    // Stub the method to return a value regardless of the String argument passed
    doReturn(new DataEntity()).when(mockRepo).fetchData(any(String.class));
}

Method Stubbing

Stubbing defines the behavior of a mock method when it is invoked. This isolates the test code from external dependencies or complex logic.

Return Value Stubbing

The traditional syntax when(mock.method()).thenReturn(value) is widely used. For chaining multiple return values for consecutive calls, you can pass multiple arguments to thenReturn or chain them fluently.

import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertEquals;

@Test
void chainedStubbing() {
    // Define behavior for the 1st, 2nd, and 3rd calls
    when(mockRepo.getCount(any()))
        .thenReturn(10)
        .thenReturn(20)
        .thenReturn(30);
        
    // Alternatively: when(mockRepo.getCount(any())).thenReturn(10, 20, 30);

    assertEquals(10, mockRepo.getCount("test"));
    assertEquals(20, mockRepo.getCount("test"));
    assertEquals(30, mockRepo.getCount("test"));
}

Exception Stubbing

To simulate error conditions, methods can be stubbed to throw exceptions. The doThrow() syntax is preferred, especially for Spy objects or void methods.

import static org.mockito.Mockito.doThrow;

@Test
void exceptionStubbing() {
    doThrow(new RuntimeException("Database failure")).when(mockRepo).fetchData(any());
}

Invoking Real Methods

When using Spies, or occasionally with Mocks, you may wish to execute the actual implementation of a method. This is achieved using thenCallRealMethod() or doCallRealMethod().

import static org.mockito.Mockito.when;
import static org.mockito.Mockito.spy;

@Test
void realMethodExecution() {
    DataRepository realRepo = new DataRepository();
    DataRepository partialMock = spy(realRepo);

    // Call the actual method implementation
    when(partialMock.getInternalState()).thenCallRealMethod();
}

Tags: java JUnit 5 Mockito Unit Testing TDD

Posted on Tue, 02 Jun 2026 17:44:35 +0000 by Wykster