
Mockito 模拟 List 时,仅 stub get() 方法无法改变 size() 或 isEmpty() 的默认返回值,导致逻辑矛盾;真正的解决方案是——直接使用真实不可变列表(如 List.of()),既简洁又可靠。
mockito 模拟 list 时,仅 stub `get()` 方法无法改变 `size()` 或 `isempty()` 的默认返回值,导致逻辑矛盾;真正的解决方案是——直接使用真实不可变列表(如 `list.of()`),既简洁又可靠。
在单元测试中,盲目遵循“所有协作对象必须被 mock”的规则,反而会引入隐蔽缺陷。正如示例中 Sum.sum() 方法所示:它通过 list.size() 和 list.get(i) 遍历求和,而 Mockito.mock(List.class) 创建的是一个无行为骨架对象——其 size() 默认返回 0(int 类型默认值),isEmpty() 默认返回 false(boolean 默认值),即使你显式 stub 了 get(0),也无法让 size() != 0 或 isEmpty() == false 成立。这导致循环体 for (int i = 0; i 根本不执行,结果恒为 0,与预期严重不符。
更关键的是,这种 mock 破坏了列表的核心契约:一个 !isEmpty() 但 size() == 0 的对象,在 Java 中根本不是合法的 List 实例——它是一个“僵尸对象”,表面看似工作,实则违背抽象语义,极易因实现细节变更(例如将 size() == 0 改为 isEmpty())而使测试失效。
✅ 最佳实践:对值对象(Value Objects)——如 List、String、LocalDateTime——应优先使用真实实例
它们不封装外部依赖或复杂行为,构造轻量、语义明确、可读性强。现代 Java 提供了安全高效的创建方式:
@Test
void sum_single() {
List<integer> input = List.of(42); // 不可变、线程安全、零开销
assertEquals(42L, Sum.sum(input));
}
@Test
void sum_multiple() {
List<integer> input = List.of(1, 2, 3, 4);
assertEquals(10L, Sum.sum(input));
}
@Test
void sum_empty() {
List<integer> input = List.of(); // 明确表达空场景
assertEquals(-1L, Sum.sum(input));
}</integer></integer></integer>
⚠️ 注意事项:
- 避免
new ArrayList()后手动add()—— 冗余且易出错;List.of()语义更清晰、性能更好; - 若需可变列表(如测试修改逻辑),用
new ArrayList(List.of(...)); - 真实列表 ≠ “集成测试”——它仍是纯内存操作,不涉及 IO、网络或数据库,完全符合单元测试边界;
- Mockito 官方明确建议:Don’t mock value objects 和 Don’t mock types you don’t own(How to write good tests)。
总结:Mock 是工具,不是教条。 单元测试的目标是验证业务逻辑的正确性,而非机械满足“单类隔离”。当 mock 带来歧义、脆弱性和额外维护成本时,果断回归真实对象——代码更短、意图更明、稳定性更高。记住 Mockito 的箴言:Don’t hesitate to not mock!










