
mockito创建的mock对象不会调用真实类的构造函数,因此构造函数中对字段的初始化(如super(colour, 18.99))完全被忽略;所有方法(包括getprice())默认返回对应类型的“空值”(如double返回null,double返回0.0),除非显式stub。
mockito创建的mock对象不会调用真实类的构造函数,因此构造函数中对字段的初始化(如super(colour, 18.99))完全被忽略;所有方法(包括getprice())默认返回对应类型的“空值”(如double返回null,double返回0.0),除非显式stub。
在使用Mockito进行单元测试时,一个常见误解是:Mock对象会模拟真实对象的生命周期,包括执行构造函数、初始化字段等行为。事实恰恰相反——Mockito的@Mock创建的是一个方法级代理对象,其底层基于字节码增强(如Byte Buddy),仅重写方法调用逻辑,完全跳过原始类的构造函数执行。
以你的Football类为例:
public class Football extends Item {
public Football(Colour colour, Double price) {
super(colour, 18.99); // ← 这行代码在mock中根本不会执行!
}
public Double getPrice() {
return price; // ← price字段从未被赋值,且getPrice()也被mock覆盖
}
}当你声明 @Mock Football football; 并调用 football.getPrice() 时:
- getPrice() 方法已被Mockito拦截;
- 由于未对该方法做任何stub(即未用 when(football.getPrice()).thenReturn(...) 配置),Mockito按规则返回默认值;
- 对于返回类型为 Double(引用类型),默认返回 null;若返回类型是 double(基本类型),则默认返回 0.0。
✅ 正确做法:根据测试意图选择合适策略:
| 场景 | 推荐方式 | 示例 |
|---|---|---|
| 需要真实行为 + 可控依赖 | 使用 @Spy(部分模拟) | @Spy Football football = new Football(Colour.RED, 25.99); → 构造函数执行,字段被初始化,仅可选择性stub个别方法 |
| 需完全控制返回值 | 显式stub方法 | when(football.getPrice()).thenReturn(18.99); |
| 需真实对象 + 无副作用构造 | 直接 new Football(...)(非mock) | 简单POJO建议优先用真实实例,更轻量、更可预测 |
⚠️ 注意事项:
- @Mock 不等于“模拟一个已初始化的对象”,而是“模拟一个可编程的行为容器”;
- MockitoAnnotations.openMocks(this) 仅完成注解注入,不触发任何构造逻辑;
- 若Item父类构造器有副作用(如校验、日志、外部调用),@Mock 下这些副作用100%不会发生——这既是安全特性,也是潜在盲点;
- 对final类、private方法、static方法等,Mockito默认无法mock(需启用mockito-inline或改用@ExtendWith(MockitoExtension.class)配合新API)。
? 总结:Mock不是“复制对象状态”,而是“接管方法契约”。若你需要构造函数逻辑生效,请选用@Spy或直接实例化;若你只需隔离依赖并控制方法输出,请始终显式stub关键方法——这是Mockito设计哲学的核心:明确优于隐式,控制优于猜测。








