Java 8迁移至Java 10时Mockito 2.20.0模块可访问性问题求助
你遇到的问题本质是Java模块系统(JPMS)的访问控制机制和Mockito的反射需求之间的冲突:在Java 8中,包私有类或未导出的公共类可以通过反射轻松访问,但JPMS严格限制了跨模块的反射访问,而Mockito因为放在类路径下,被识别为unnamed module,自然无法访问模块内未开放的包。
下面是几个符合你需求(保持类对其他模块不可见)的解决方案:
1. 用--add-opens运行时参数开放反射访问
这是最直接的解决方案,不需要修改代码结构,仅在测试阶段开放所需包的反射权限给未命名模块(包含Mockito)。
修改maven-surefire-plugin的配置,添加JVM参数:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.0</version> <configuration> <argLine> <!-- 针对你测试的场景,开放对应的包给所有未命名模块 --> --add-opens nodatafound.mockito_jpms_usecase/nodatafound.mjpmsuc.withopens=ALL-UNNAMED --add-opens nodatafound.mockito_jpms_usecase/nodatafound.mjpmsuc.internal=ALL-UNNAMED <!-- 其他需要测试的包同理添加 --> </argLine> </configuration> </plugin>
- 原理:
--add-opens是JPMS提供的运行时参数,允许指定模块的某个包对目标模块开放反射访问权限,但不会导出该包(导出会让其他模块都能正常访问,不符合你“保持不可见”的要求)。ALL-UNNAMED表示所有类路径下的未命名模块,正好覆盖你的Mockito。
2. 在module-info.java中声明包开放给Mockito
如果你的Mockito已经是自动模块(你提到它有Automatic-Module-Name),可以直接在模块描述符里指定开放特定包给Mockito,这样不需要依赖运行时参数:
module nodatafound.mockito_jpms_usecase { // 仅开放给Mockito模块,其他模块无法访问 opens nodatafound.mjpmsuc.withopens to org.mockito; opens nodatafound.mjpmsuc.internal to org.mockito; }
- 注意:如果你的maven-surefire插件还是把Mockito放在类路径(而非模块路径),这个配置可能不生效——因为类路径下的Jar会被视为
unnamed module,而不是org.mockito模块。这种情况下还是需要用方案1。
3. 升级Mockito和插件版本
你当前使用的Mockito 2.20.0对JPMS的支持还比较初期,建议升级到Mockito 3.x及以上版本(比如3.11.2),这些版本对JPMS的兼容性更好,包括对自动模块的识别、反射访问的优化。
同时,建议升级maven-surefire-plugin到3.0.0-M7及以上,新版插件对JPMS测试的支持更完善,能更好地处理模块路径和类路径的混合场景。
4. 调整类的访问级别(可选)
如果不想依赖反射开放参数,可以把包私有类改为public但放在未导出的包中——这样其他模块因为无法访问未导出的包,依然看不到这些类,而你可以通过opens或--add-opens让Mockito访问它们,效果和原来的包私有类一致。
另外,如果测试代码和被测试类在同一个模块的测试源目录(比如src/test/java),同一个模块内的测试代码默认可以访问包私有类,不需要额外配置——但这个方案只适用于模块内测试。
总结一下:最推荐的是方案1(--add-opens参数),它既满足你“保持类对其他模块不可见”的需求,又不需要修改代码结构,适配你当前的插件版本。如果后续升级了Mockito和插件,再考虑方案2或4。
内容的提问来源于stack exchange,提问作者NoDataFound

