
本文介绍如何使用mockito对set集合中的每个对象进行mock,并在单元测试中准确验证其方法是否被逐一调用,避免因set去重机制导致的验证失败。
在单元测试中,当我们需要验证一个集合(如 Set)中每个元素的方法是否被调用时,不能直接使用相同 mock 实例多次添加进 Set——因为 Mockito.mock(B.class) 创建的 mock 对象默认基于 Object#hashCode() 和 equals() 进行比较,而默认 mock 的 identity-based 行为会导致多个相同 mock 被视为重复元素,最终 Set 中仅保留一个。这将使 a.aMethod() 实际只遍历一次,导致后续 verify() 断言失败或漏检。
✅ 正确做法是:为 Set 中每个位置创建独立的 mock 实例。如下所示:
@Test
public void givenAWithBSet_whenAMethodIsCalled_thenCallBMethodOnAllBs() {
Set bSet = new HashSet<>();
// 创建多个独立的 mock B 实例(不可复用同一个 mock!)
bSet.add(Mockito.mock(B.class));
bSet.add(Mockito.mock(B.class));
// 或使用循环批量创建(推荐用于数量较多场景)
// for (int i = 0; i < 5; i++) {
// bSet.add(Mockito.mock(B.class));
// }
A a = new A(bSet);
// 执行待测方法
a.aMethod();
// 验证每个 mock 的 bMethod 是否被调用恰好一次
for (B b : a.getBSet()) {
verify(b).bMethod(); // 等价于 verify(b, times(1)).bMethod()
}
// (可选)进一步确保无其他意外调用
a.getBSet().forEach(Mockito::verifyNoMoreInteractions);
}⚠️ 注意事项:
- ❌ 错误示例:B mockB = mock(B.class); bSet.add(mockB); bSet.add(mockB); → Set 实际只含 1 个元素;
- ✅ 正确原则:n 个待验证元素 ⇒ 创建 n 个独立 mock(B.class);
- 若 B 类有复杂依赖或需定制行为(如返回值),可在 mock() 后用 when(...).thenReturn(...) 配置;
- 使用 Lombok 的 @Getter 时,确保 getBSet() 返回的是原始 Set 引用(非副本),否则 verify 将作用于不可达对象;若需防御性拷贝,应在 verify 前改用 a.getBSet().stream().toList() 等方式捕获快照。
总结:Mock Set 中的对象本质是 Mock 多个独立协作单元,核心在于保证集合元素的唯一性与可验证性统一。只要为每个逻辑上“不同的 B”创建专属 mock,即可精准驱动和断言其行为,实现真正可靠的单元测试覆盖。










