
本文介绍在无法重构代码的前提下,使用 Mockito 3.5+ 的 mockConstruction 特性,精准验证 Class1.myMethod() 中为每个列表元素创建一个 Class2 实例并调用其 anotherMethod() 的完整测试方案。
本文介绍在无法重构代码的前提下,使用 mockito 3.5+ 的 `mockconstruction` 特性,精准验证 `class1.mymethod()` 中为每个列表元素创建一个 `class2` 实例并调用其 `anothermethod()` 的完整测试方案。
在单元测试中,当被测类(如 Class1)直接通过 new Class2(...) 构造依赖对象(Class2),且因项目约束无法修改构造逻辑(例如注入工厂或接口)时,传统 Mockito 的 spy() 或 mock() 无法拦截对象创建过程——因为 spy() 只能代理已有实例的行为,而 new 表达式始终生成全新真实对象。
幸运的是,Mockito 3.5.0 引入了 mockConstruction() API,专为解决此类“不可控对象构造”问题而设计。它能在运行时拦截指定类的所有构造调用,并为每次构造返回一个受控的 mock 实例,同时提供上下文(如传入的构造参数),极大提升了对隐式依赖的可测性。
以下是一个完整、可运行的测试示例:
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import java.util.Arrays;
import java.util.List;
class Class1Test {
@Test
void myMethod_createClass2PerItemAndCall_anotherMethod() {
// 使用 try-with-resources 确保自动清理 mock 构造上下文
try (MockedConstruction<class2> mocked = mockConstruction(Class2.class,
(mock, context) -> {
// 可选:预设 mock 行为,例如返回构造参数用于后续断言
when(mock.getElement()).thenReturn((String) context.arguments().get(0));
})) {
Class1 class1 = new Class1();
class1.myMethod(Arrays.asList("a", "b", "c")); // 输入 3 个元素
// 断言:恰好构造了 3 个 Class2 实例
List<class2> constructedInstances = mocked.constructed();
assertEquals(3, constructedInstances.size());
// 可选:验证每个实例是否按预期参数构造
assertEquals("a", constructedInstances.get(0).getElement());
assertEquals("b", constructedInstances.get(1).getElement());
assertEquals("c", constructedInstances.get(2).getElement());
// 核心验证:每个构造出的 Class2 实例都调用了 anotherMethod()
verify(constructedInstances.get(0)).anotherMethod();
verify(constructedInstances.get(1)).anotherMethod();
verify(constructedInstances.get(2)).anotherMethod();
}
}
}</class2></class2>
⚠️ 关键注意事项:
-
mockConstruction()是静态工具方法,必须配合try-with-resources使用,以确保 mock 生命周期受控,避免测试污染; -
Class2需暴露一个非私有方法(如getElement())供测试读取构造参数——这是验证构造正确性的常用技巧(也可通过context.arguments()直接获取,但需类型强转); - 该特性仅适用于 Java 8+ 且 Mockito ≥ 3.4.0,与题设的 Java 11 + Mockito 3.5 完全兼容;
- 若
Class2存在无参构造器或复杂继承链,请确保mockConstruction(Class2.class)明确指向目标类,避免误匹配父类。
✅ 总结:mockConstruction() 不是“绕过设计缺陷的权宜之计”,而是 Mockito 对现实工程约束的务实支持。它让开发者能在不破坏原有架构的前提下,实现对对象创建逻辑的端到端行为验证——既保障了测试覆盖率,也为主动推动后续可测性重构(如引入 Class2Factory)提供了坚实依据。










