Mockito测试抛出java.lang.ExceptionInInitializerError问题求助
Mockito单元测试抛出java.lang.ExceptionInInitializerError异常排查与解决
异常堆栈信息
java.lang.ExceptionInInitializerError at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167) at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217) at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145) at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117) at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109) at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105) at org.mockito.cglib.proxy.Enhancer.<clinit>(Enhancer.java:70) at org.mockito.internal.creation.cglib.ClassImposterizer.createProxyClass(ClassImposterizer.java:95) at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:57) at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49) at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24) at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33) at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59) at org.mockito.Mockito.mock(Mockito.java:1285) at org.mockito.Mockito.mock(Mockito.java:1163) at com.in28minutes.business.TodoBusinessImplTest_withMock.testWithMock(TodoBusinessImplTest_withMock.java:20) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) at java.base/java.lang.reflect.Method.invoke(Method.java:577) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:93) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210) Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @685cb137 at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:200) at java.base/java.lang.reflect.Method.setAccessible(Method.java:194) at org.mockito.cglib.core.ReflectUtils$2.run(ReflectUtils.java:57) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at org.mockito.cglib.core.ReflectUtils.<clinit>(ReflectUtils.java:47) ... 38 more
相关代码
TodoService.java
package com.in28minutes.data.api; import java.util.List; public interface TodoService { public List<String> retrieveTodos(String user); }
TodoBusinessImpl.java
import java.util.ArrayList; import java.util.List; import com.in28minutes.data.api.TodoService; public class TodoBusinessImpl { private TodoService todoService; public TodoBusinessImpl(TodoService todoService) { this.todoService = todoService; } public List<String> retrieveTodosRelatedToSpring(String user){ List<String> filteredTodos = new ArrayList<String>(); List<String> alltodos = todoService.retrieveTodos(user); for(String todo : alltodos) { if(todo.contains("Spring")) { filteredTodos.add(todo); } } return filteredTodos; } }
TodoBusinessImplTest_withMock.java
package com.in28minutes.business; import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.List; import org.junit.Test; import com.in28minutes.data.api.TodoService; public class TodoBusinessImplTest_withMock { @Test public void testWithMock() { TodoService todoServiceMock = mock(TodoService.class); List<String> todo = Arrays.asList("Learn Spring MVC", "Learn Spring", "Learn To Code"); when(todoServiceMock.retrieveTodos("Dummy")).thenReturn(todo); TodoBusinessImpl todoBusinessImpl = new TodoBusinessImpl(todoServiceMock); List<String> filteredTodos = todoBusinessImpl.retrieveTodosRelatedToSpring("Dummy"); assertEquals(2, filteredTodos.size()); } }
问题原因与解决方案
从堆栈的Caused by部分可以看出,核心问题是JDK 9+的模块系统阻止了老版本Mockito依赖的CGLIB框架进行反射访问:老版本Mockito用CGLIB尝试修改java.lang.ClassLoader的方法访问权限,但JDK 9+的模块系统不允许未命名模块访问java.base模块的内部API。
方案1:升级Mockito版本(推荐)
使用Mockito 3.0及以上版本,这些版本已适配JDK 9+模块系统,改用ByteBuddy生成代理类,彻底避免反射访问受限问题。
如果用Maven,更新依赖:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.11.0</version> <!-- 可替换为最新稳定版 --> <scope>test</scope> </dependency>
如果用Gradle:
testImplementation 'org.mockito:mockito-core:4.11.0'
方案2:添加JVM启动参数(临时绕过)
若暂时无法升级Mockito,可在运行测试时添加JVM参数,允许未命名模块访问java.base的相关包:
--add-opens java.base/java.lang=ALL-UNNAMED
在Eclipse中设置方式:右键测试类 → Run As → Run Configurations → Arguments标签页,在VM arguments中添加上述参数。
注意:此方法仅为临时绕过限制,长期建议使用方案1升级Mockito版本。
内容的提问来源于stack exchange,提问作者d_hrishi
相关产品推荐
相关产品推荐

