
本文介绍在 junit + mockito 环境下,如何对包含 `set
在面向对象单元测试中,当待测类(如 A)持有 Set 类型的依赖集合,且需验证该集合中每个元素的方法是否被调用一次时,直接使用真实对象(new B())会导致 verify() 失败——因为真实对象未被 Mockito 增强,无法追踪调用行为。此时必须使用 Mockito.mock(B.class) 创建可验证的模拟对象。
但需特别注意:Set 的去重机制基于 equals() 和 hashCode()。而默认情况下,Mockito.mock() 创建的 mock 对象使用 Object 的 equals()(即引用相等),因此若重复添加同一 mock 实例(如 mockB),Set 会将其视为重复元素,最终集合大小为 1,导致验证逻辑失效。
✅ 正确做法是:为集合中的每个位置独立创建一个新的 mock 实例,确保它们在 Set 中互不冲突,同时支持独立验证:
@Test
public void givenAWithBSet_whenAMethodIsCalled_thenCallBMethodOnAllBs() {
// GIVEN: 构建含多个独立 mock 的 Set
Set bSet = new HashSet<>();
for (int i = 0; i < 2; i++) {
bSet.add(Mockito.mock(B.class)); // 每次调用生成新 mock 实例
}
A a = new A(bSet);
// WHEN: 执行被测方法
a.aMethod();
// THEN: 遍历 a 中持有的 bSet(推荐,避免依赖原始 bSet 变量作用域)
for (B b : a.getBSet()) {
verify(b).bMethod(); // 验证每个 mock 的 bMethod 被调用一次
}
}? 关键实践提示:
- ✅ 始终从 a.getBSet() 获取集合进行遍历验证,而非原始 bSet 变量——这更符合封装原则,也避免因变量作用域或引用变更引发的潜在问题;
- ⚠️ 不要复用同一个 mock 实例插入 Set(如 B mockB = mock(B.class); bSet.add(mockB); bSet.add(mockB);),否则 Set 实际仅含 1 个元素;
- ✅ 若需验证调用次数精确性(如“恰好一次”),verify(b).bMethod() 默认即等价于 verify(b, times(1)).bMethod(),简洁写法已足够;
- ? 如需进一步确保无意外调用,可在循环后补充 verifyNoMoreInteractions(b)(按需添加,非必需);
- ? 若 B 类有构造参数或需 stub 行为,可结合 mock(B.class, withSettings().defaultAnswer(CALLS_REAL_METHODS)) 或 when(...).thenReturn(...) 进行增强。
综上,核心在于理解 Set 的语义约束与 mock 对象的身份特性,并据此设计出可重复、可验证、符合集合行为的测试数据。这一模式同样适用于 List、Map 等集合类型中对元素行为的批量断言场景。










