如何让JUnit5与JDK10(Jigsaw)及Maven3协同工作?
这个InaccessibleObjectException的根源很清晰:你的主代码模块de.truncated.framework.shared只通过exports对外暴露了de.truncated.framework.shared.resources包,但JUnit5需要通过反射实例化你的测试类,而反射访问需要模块显式opens包给调用方——这里你的测试代码属于「未命名模块(unnamed module)」,所以模块系统拒绝了反射访问。JDK10移除--permit-illegal-access是模块化规范的必然,我们需要用合规的方式解决。
下面是三种不同场景下的解决方案,按推荐程度排序:
方案1:修改主模块的module-info.java,opens测试包给未命名模块(最直接)
JUnit需要反射创建ResourceManagerTest实例,你只需在主模块的module-info.java中,把测试类所在的包opens给未命名模块即可:
module de.truncated.framework.shared { // 保留原有的exports语句,保证正常编译/运行访问 exports de.truncated.framework.shared.resources; // 添加这行,允许未命名模块反射访问该包 opens de.truncated.framework.shared.resources to ALL-UNNAMED; }
如果想更严谨,也可以只开放给JUnit的核心模块(比如org.junit.jupiter.engine、org.junit.platform.commons),但测试阶段用ALL-UNNAMED足够简洁高效。
方案2:将测试代码模块化(最规范的模块化实践)
如果你的项目想完全遵循Jigsaw模块化规范,可以给测试代码创建独立的模块:
- 在
src/test/java下创建测试模块的module-info.java:
module de.truncated.framework.shared.test { // 依赖主业务模块 requires de.truncated.framework.shared; // 依赖JUnit5核心模块 requires org.junit.jupiter.api; requires org.junit.jupiter.engine; // 声明测试类所在包开放给JUnit引擎(因为需要反射实例化) opens de.truncated.framework.shared.resources to org.junit.jupiter.engine; }
- 调整主模块的
module-info.java,只开放给测试模块(更安全):
module de.truncated.framework.shared { exports de.truncated.framework.shared.resources; // 仅向测试模块开放反射权限 opens de.truncated.framework.shared.resources to de.truncated.framework.shared.test; }
这种方式能严格控制模块间的访问权限,适合大型模块化项目长期维护。
方案3:通过Maven Surefire插件添加JVM参数(无需修改模块代码)
如果你不想修改模块定义,可以在Maven Surefire插件中添加--add-opens参数,临时让主模块开放包给未命名模块:
修改你的maven-surefire-plugin配置,添加<argLine>节点:
<plugin> <artifactId>maven-surefire-plugin</artifactId> <version>${surefire.version}</version> <configuration> <!-- 新增JVM参数,强制开放包给未命名模块 --> <argLine>--add-opens de.truncated.framework.shared/de.truncated.framework.shared.resources=ALL-UNNAMED</argLine> </configuration> <dependencies> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit.platform.version}</version> </dependency> <dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>${asm.version}</version> </dependency> </dependencies> </plugin>
这个参数仅在测试运行时生效,适合快速验证或临时场景。
额外注意事项
- 你的Maven Surefire版本(2.21.0)对JDK10模块化的支持足够,但如果后续遇到其他兼容性问题,可以尝试升级到3.0.0-M7及以上版本,对JUnit5和模块化的适配更完善。
- 确保
maven-compiler-plugin已经正确启用模块化编译——你当前配置的source和target为10,这已经满足要求,3.7.0版本会自动处理模块路径。
内容的提问来源于stack exchange,提问作者Jan S.

