核心是测试模块需声明requires被测模块并用opens授权反射访问,构建运行须启用--module-path而非-classpath。junit 5测试需显式opens测试包到org.junit.jupiter,maven/ide须配置模块化支持以确保jpms约束生效。

在单元测试中测试 Java 9 模块化项目,核心在于让测试代码能正确访问被测模块的导出包,同时不破坏模块封装边界。这和传统非模块化项目的测试方式有本质区别——不能仅靠 classpath 解决依赖和可见性问题。
确保测试模块能读取被测模块
JUnit 测试本身需运行在模块上下文中。若被测项目已定义 module-info.java,测试类所在的模块(通常是自动模块或显式测试模块)必须声明对它的 requires 依赖。
- 推荐做法:为测试代码单独创建一个测试模块,例如
module-info-test.java(需配合构建工具支持),并在其中写:module com.example.mymodule.test {<br> requires com.example.mymodule;<br> requires org.junit.jupiter;<br> opens com.example.mymodule.test to org.junit.jupiter;<br>} - 更常见做法(Maven + JUnit 5):保持测试在
src/test/java下,不写module-info.java,此时测试类作为“自动模块”加载,可访问所有导出包,但需确保编译和运行时使用--module-path而非-cp。
处理反射与内部类访问
当测试需要访问被测模块中未导出的包(如 com.example.mymodule.internal),或需通过反射调用 package-private 方法时,仅靠 exports 不够,得用 opens。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 在被测模块的
module-info.java中添加:opens com.example.mymodule.internal to org.junit.jupiter; - 如果测试框架是自定义类加载器或非标准运行环境,可能还需加
open整个包(慎用):open com.example.mymodule.internal; - JUnit 5 默认使用自己的模块名
org.junit.jupiter,因此to后必须写对,不能写成to junit.jupiter或省略。
构建与运行时配置要匹配模块路径
Maven 或 Gradle 必须启用模块化支持,否则 module-info.java 会被忽略,测试将退化为传统 classpath 行为,失去模块验证意义。
- Maven 编译插件需设置:
<compilerargs><br> <arg>--module-path</arg><br> <arg>${project.build.outputDirectory}</arg><br></compilerargs> - 运行测试时,Maven Surefire 插件需配置:
<argline>--module-path ${project.build.outputDirectory} --add-modules ALL-SYSTEM</argline> - IDE(如 IntelliJ)需开启 “Use --module-path for compilation and test execution” 选项,否则调试时模块关系不生效。
验证模块依赖是否真正生效
光跑通测试不够,要确认模块系统确实在起作用。可在测试中主动触发模块约束失败场景:
- 尝试 new 一个未导出包中的类,应编译失败或运行时报
IllegalAccessError; - 故意删掉被测模块
requires java.logging,再在代码中调用Logger.getLogger(),看编译是否报错; - 用
jdeps --module-path mods --require com.example.mymodule检查依赖图,确认无隐式依赖残留。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










