Java模块化集成测试需声明测试模块依赖、用Testcontainers启动真实服务、统一模块路径运行、通过ServiceLoader验证服务契约。

Java 模块化系统(即 Java Platform Module System,JPMS)中编写跨模块的集成测试,核心难点在于模块封装性限制了类的可见性、服务发现和资源访问。直接用传统方式写测试容易遇到 IllegalAccessError、Module not found、class not visible to module 等问题。关键不是“能不能测”,而是“怎么让测试代码合法地穿透模块边界,同时保持真实集成行为”。
以下是从实践出发的四个关键方向:
明确测试模块的声明与依赖
测试代码本身需要作为独立模块(如 `cim.integration.test`),并在 `module-info.java` 中显式声明对被测模块的 `requires`,以及必要的 `opens` 或 `exports`: ```java // src/test/java/module-info.java module cim.integration.test { requires cim.server; requires cim.route; requires cim.client; requires org.junit.jupiter; requires org.testcontainers;// 允许测试反射访问被测模块的包(必要时) opens com.cim.server.integration to org.junit.jupiter; opens com.cim.route.config to org.junit.jupiter;
}
⚠️ 注意:`opens` 是运行时允许反射访问的关键;`exports` 仅在需被其他模块主动调用时才用(测试模块通常不需要导出)。
<H3>用 Testcontainers 启动真实依赖服务</H3>
跨模块集成常涉及多进程协作(如 `cim-server` + `cim-route` + ZooKeeper)。不能靠 `@SpringBootTest` 单模块启动模拟——必须还原真实部署拓扑:
- 为每个被测模块准备对应的 Docker 容器(ZooKeeper、Redis、PostgreSQL 等)
- 使用 `@Container` 声明静态容器实例(Testcontainers 推荐用 `@Testcontainers` 类级注解)
- 在 `@BeforeAll` 中等待所有容器就绪,并注入配置(如 `cim.route` 读取 `zookeeper.connect=cim-zk:2181`)
示例:
```java
@Testcontainers
class MultiModuleIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("cim_test");
@Container
static ZooKeeperContainer zookeeper = new ZooKeeperContainer("zookeeper:3.9.2");
@Test
void whenClientSendsMessage_thenServerAndRouteProcessIt() {
// 启动 cim-server(通过 Maven exec 或嵌入式方式)
// 启动 cim-route(连接 zookeeper)
// 启动 cim-client(连接 route)
// 执行端到端消息流转断言
}
}避免在测试中绕过模块系统
不要用 `-p`(--module-path)配合 `-cp`(--class-path)混用方式强行加载类——这会破坏模块图一致性,导致 `ServiceLoader` 失效或 `ServiceConfigurationError`。正确做法是: - 测试模块和被测模块统一放在 `--module-path` 下 - 使用 `java --module cim.integration.test/cim.integration.test.MainTest` 方式运行 - 构建时用 Maven 的 `maven-compiler-plugin` 和 `maven-surefire-plugin` 显式配置模块参数: ```xml服务接口层做契约驱动测试
跨模块交互往往通过 `module-info.java` 中的 `uses` / `provides` 声明服务。集成测试应验证服务契约是否真正履行: - 被测模块 `cim.route` 声明 `provides RouteService with ZooKeeperRouteServiceImpl` - 测试模块 `requires cim.route` 并 `uses RouteService` - 在测试中通过 `ServiceLoader.load(RouteService.class)` 获取实例,再调用其方法断言行为这样既尊重 JPMS 设计,又覆盖了模块间服务发现的真实路径,比直接 new 实现类更贴近生产环境。
立即学习“Java免费学习笔记(深入)”;
不复杂但容易忽略。


















