Mockito
Spring Boot
JDK 11
Testing
Migration Issues

Mockito error in Spring Boot tests after migrating to JDK 11

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Moving a Spring Boot project from Java 8 to JDK 11 often breaks tests before it breaks production code. Mockito-based tests are a common pain point because the upgrade exposes older dependencies, reflective-access assumptions, and outdated JUnit integration patterns that were tolerated on older JDKs.

The fix is usually not one JVM flag or one annotation change. You need to check dependency versions, modernize how mocks are initialized, and separate plain Mockito tests from Spring context tests.

Why JDK 11 Exposes Mockito Problems

Mockito creates mocks through bytecode generation and reflective access. JDK 11 did not make Mockito unusable, but it did make old testing stacks much less forgiving. Teams commonly see:

  • 'InaccessibleObjectException'
  • illegal reflective access warnings
  • failures when mocking final classes or JDK types
  • tests that work in one module but fail in another because the dependency graph differs

In practice, the root cause is often one of these:

  • 'mockito-core is too old'
  • 'byte-buddy is too old'
  • JUnit 4 and JUnit 5 styles are mixed
  • 'MockitoAnnotations.initMocks is still being used from old examples'
  • PowerMock or another library is doing deeper bytecode tricks than Mockito itself

Align The Test Dependencies First

If Spring Boot manages your dependency versions, let it. Many migration bugs come from overriding Mockito or JUnit versions partially and ending up with a stack that compiles but behaves badly at runtime.

A clean Gradle setup for JUnit 5 looks like this:

gradle
1dependencies {
2    testImplementation("org.springframework.boot:spring-boot-starter-test")
3    testImplementation("org.mockito:mockito-junit-jupiter")
4}
5
6tasks.test {
7    useJUnitPlatform()
8}

That does two important things:

  1. it keeps Spring Boot's managed test dependencies in place
  2. it ensures the test task is actually using the JUnit Platform

Without useJUnitPlatform(), developers sometimes think Mockito is failing when the real issue is that the wrong test engine is executing the suite.

Prefer The JUnit 5 Mockito Extension

For plain unit tests, the most robust setup is to use the Mockito JUnit 5 extension instead of manually opening mocks:

java
1import static org.assertj.core.api.Assertions.assertThat;
2import static org.mockito.Mockito.when;
3
4import org.junit.jupiter.api.Test;
5import org.junit.jupiter.api.extension.ExtendWith;
6import org.mockito.InjectMocks;
7import org.mockito.Mock;
8import org.mockito.junit.jupiter.MockitoExtension;
9
10@ExtendWith(MockitoExtension.class)
11class GreetingServiceTest {
12
13    @Mock
14    private UserRepository userRepository;
15
16    @InjectMocks
17    private GreetingService greetingService;
18
19    @Test
20    void returnsGreetingForKnownUser() {
21        when(userRepository.findNameById(7L)).thenReturn("Mina");
22
23        assertThat(greetingService.greet(7L)).isEqualTo("Hello, Mina");
24    }
25}

This avoids a lot of fragile setup code and matches the current Mockito style much better than older runner-based examples.

Use openMocks If You Need Manual Lifecycle Control

Sometimes you do not want to use the extension directly. In that case, use MockitoAnnotations.openMocks(this) rather than the older initMocks pattern:

java
1import org.junit.jupiter.api.AfterEach;
2import org.junit.jupiter.api.BeforeEach;
3import org.mockito.MockitoAnnotations;
4
5class ManualMockitoTest {
6    private AutoCloseable mocks;
7
8    @BeforeEach
9    void setUp() {
10        mocks = MockitoAnnotations.openMocks(this);
11    }
12
13    @AfterEach
14    void tearDown() throws Exception {
15        mocks.close();
16    }
17}

That may look minor, but migration problems often come from copying older snippets that predate current Mockito guidance.

Know When Spring Boot Should Mock For You

A plain Mockito unit test is not the same thing as a Spring Boot test that loads an application context. If the Spring container is involved, you often want @MockBean so that Spring replaces the real bean in the context.

java
1import static org.mockito.BDDMockito.given;
2import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
3import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
4
5import org.junit.jupiter.api.Test;
6import org.springframework.beans.factory.annotation.Autowired;
7import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
8import org.springframework.boot.test.mock.mockito.MockBean;
9import org.springframework.test.web.servlet.MockMvc;
10
11@WebMvcTest(GreetingController.class)
12class GreetingControllerTest {
13
14    @Autowired
15    private MockMvc mockMvc;
16
17    @MockBean
18    private GreetingService greetingService;
19
20    @Test
21    void endpointReturnsOk() throws Exception {
22        given(greetingService.greet(7L)).willReturn("Hello, Mina");
23
24        mockMvc.perform(get("/greetings/7"))
25            .andExpect(status().isOk());
26    }
27}

If you use plain @Mock in a Spring context test, the application may still wire the real bean, and the failure can look unrelated to mocking.

When The Real Problem Is Not Mockito

A JDK 11 migration often reveals deeper issues in the test stack. PowerMock is a frequent example because it depends heavily on internals and can fail in situations where modern Mockito works fine. Another case is adding a pile of --add-opens flags to keep old tests limping along. That can be a temporary bridge, but it is usually a sign that the real fix is dependency modernization.

The right debugging order is:

  1. confirm the exact failing dependency versions
  2. isolate whether the failure happens in plain Mockito or only in Spring Boot tests
  3. check whether PowerMock or another older tool is involved
  4. remove unnecessary custom JVM flags before assuming the JDK itself is broken

Common Pitfalls

  • Upgrading the JDK without upgrading the test dependency stack.
  • Mixing JUnit 4 runners and JUnit 5 extensions in the same suite.
  • Using initMocks from older examples instead of current Mockito initialization patterns.
  • Using plain Mockito mocks in a Spring context test when @MockBean is needed.
  • Blaming Mockito for failures that actually come from PowerMock, Byte Buddy version drift, or stale build configuration.

Summary

  • JDK 11 often exposes old Mockito and test-stack assumptions rather than creating a brand-new Mockito problem.
  • Start by aligning Spring Boot, Mockito, Byte Buddy, and JUnit versions.
  • Prefer @ExtendWith(MockitoExtension.class) for plain unit tests.
  • Use MockitoAnnotations.openMocks if you need manual initialization.
  • In Spring Boot context tests, use @MockBean when the container must replace a real bean.

Course illustration
Course illustration

All Rights Reserved.