
本文详解如何使用 Mockito 5.0+ 的 mockConstruction 配合上下文回调,对被测代码中通过 new 创建的对象的构造参数进行精确验证,避免侵入式改造,实现真正的单元测试隔离。
本文详解如何使用 mockito 5.0+ 的 `mockconstruction` 配合上下文回调,对被测代码中通过 `new` 创建的对象的构造参数进行精确验证,避免侵入式改造,实现真正的单元测试隔离。
在单元测试中,当被测类(如 Target)直接通过 new 关键字实例化依赖对象(如 Mocked)时,传统 Mock 工具难以拦截构造调用——因为构造行为不属于接口或可 mock 的方法。自 Mockito 3.5.0 引入、并在 5.0+ 稳定增强的 mockConstruction() API,正是为此类场景设计的核心解决方案。
关键在于:mockConstruction(Class
- mock:已创建的模拟实例(通常无需操作);
- context:MockedConstruction.Context
类型对象,封装了本次构造调用的完整元信息,其中 context.arguments() 即为按声明顺序传入构造器的参数列表(List。
因此,验证构造参数的本质,就是对 context.arguments() 进行类型安全的断言。以下为完整、可运行的示例:
@Test
void test_Target_constructs_Mocked_with_correct_arguments() {
try (MockedConstruction<Mocked> mocked = mockConstruction(
Mocked.class,
(mock, context) -> {
// 断言构造参数(注意类型转换)
assertThat((String) context.arguments().get(0)).isEqualTo("Hello");
assertThat((String) context.arguments().get(1)).isEqualTo("World");
assertThat((Integer) context.arguments().get(2)).isEqualTo(99);
}
)) {
new Target().testMe(); // 触发构造
}
}✅ 优势说明:
- 零侵入:无需修改 Mocked 类(如添加 setter、静态工厂或依赖注入);
- 精准定位:每个 mockConstruction 块内,回调会为每次 new Mocked(...) 调用独立触发,支持多次构造的分别验证;
- 类型安全提示:虽需显式强转,但结合现代 IDE 和 assertThat 链式断言,可保障可读性与健壮性。
⚠️ 重要注意事项:
- context.arguments() 返回的是 Object 列表,必须按构造器参数声明顺序访问索引(get(0) 对应第一个参数),且需谨慎处理基本类型(如 int 会被自动装箱为 Integer,故应转为 Integer 而非 int);
- 若被测逻辑中存在多次构造(如循环创建),可通过 mocked.constructed().size() 获取总次数,并在回调中结合 context.getCount() 区分第几次调用;
- mockConstruction 仅适用于 非 final 类(final 类无法被字节码增强),且要求测试运行在支持 Java Agent 或 Byte Buddy 的环境中(Maven Surefire 默认满足);
- 不要遗漏 try-with-resources —— 它确保 mock 生命周期正确结束,避免跨测试污染。
总结而言,mockConstruction 的回调机制将“构造参数验证”从“无法测试”的困境,转变为简洁、声明式的断言流程。它不是对构造器本身的 mock(构造器不可 mock),而是对构造行为发生时的上下文进行实时捕获与校验——这正是 Mockito 在解耦测试与实现细节上的精妙设计。










