当前位置: 首页 > article >正文

2.1 Mockito核心API详解

Mockito核心API详解

1. 创建Mock对象

Mockito提供两种方式创建模拟对象:

1.1 手动创建(传统方式)
// 创建接口/类的Mock对象
UserDao userDao = Mockito.mock(UserDao.class);
1.2 注解驱动(推荐方式)

结合JUnit 5的扩展机制,自动管理Mock生命周期:

@ExtendWith(MockitoExtension.class) // 启用Mockito支持
class UserServiceTest {

    @Mock // 自动创建Mock对象
    private UserDao mockUserDao;

    @InjectMocks // 自动注入@Mock对象到被测类
    private UserService userService;
}

选择策略

  • 简单测试 → 手动创建
  • 多依赖测试 → 注解驱动(避免重复代码)

2. 方法桩(Stubbing)

控制Mock对象方法的返回值或异常抛出。

2.1 基础配置
// 返回固定值
when(mockUserDao.findById(1)).thenReturn(new User("Alice"));

// 抛出异常
when(mockUserDao.save(any())).thenThrow(new DatabaseException());

// 连续配置(依次返回)
when(mockList.get(0)).thenReturn("A", "B", "C"); // 第一次"A",第二次"B"...
2.2 动态响应
// 根据输入参数动态计算返回值
when(mockCalculator.add(anyInt(), anyInt())).thenAnswer(invocation -> {
    int a = invocation.getArgument(0);
    int b = invocation.getArgument(1);
    return a + b;
});

// 调用真实方法(部分保留逻辑)
when(mockUserDao.findById(1)).thenCallRealMethod();
2.3 Void方法处理
// 默认不做任何事情
doNothing().when(mockLogger).writeLog(anyString());

// 抛出异常
doThrow(new IOException()).when(mockFileService).deleteFile(any());

3. 验证交互行为

验证Mock对象的方法是否按预期被调用。

3.1 基础验证
verify(mockUserDao).findById(1); // 验证方法被调用一次
3.2 调用次数验证
verify(mockUserDao, times(2)).update(any()); // 精确次数
verify(mockUserDao, atLeastOnce()).delete(5); // 至少一次
verify(mockUserDao, never()).findAll(); // 从未调用
3.3 顺序验证
InOrder inOrder = inOrder(mockA, mockB);
inOrder.verify(mockA).prepare();
inOrder.verify(mockB).execute();
3.4 超时验证(异步场景)
// 200ms内至少调用一次
verify(mockAsyncService, timeout(200)).callback();

4. 参数匹配器(Argument Matchers)

灵活匹配方法参数,增强测试的适应性和可读性。

4.1 内置匹配器
// 任意字符串参数
when(mockUserDao.findByUsername(anyString())).thenReturn(...);

// 混合精确与模糊匹配
when(service.process(eq("order"), anyInt())).thenReturn(true);
4.2 自定义匹配器
// 定义复杂参数条件
verify(mockValidator).validate(argThat(user -> 
    user.getAge() > 18 && user.isVerified()
));

注意事项

  • 若方法中多个参数使用匹配器,所有参数必须都用匹配器
  • 避免过度使用any() → 可能导致测试条件过于宽松

5. 高级功能API
5.1 Spy对象(部分真实对象)
List<String> realList = new ArrayList<>();
List<String> spyList = spy(realList); // 保留真实方法

// 覆盖特定方法行为
doReturn(false).when(spyList).isEmpty(); 
5.2 参数捕获(ArgumentCaptor)
@Captor // 自动初始化
private ArgumentCaptor<User> userCaptor;

@Test
void testSaveUser() {
    userService.register("Bob");
    verify(mockUserDao).save(userCaptor.capture());
    
    User capturedUser = userCaptor.getValue();
    assertEquals("Bob", capturedUser.getName());
}
5.3 重置Mock状态
reset(mockUserDao); // 慎用!通常表示测试设计有问题

核心API对照表

API典型应用场景
mock() / @Mock创建完全模拟的假对象
when().thenReturn()配置方法返回固定值
verify()验证方法调用情况
any() / eq()参数灵活匹配
@Spy需要保留部分真实逻辑的对象
ArgumentCaptor深入分析传入方法的参数内容

最佳实践建议

  1. 优先使用注解驱动
    @Mock + @InjectMocks 提升代码可读性和维护性。
  2. 精确验证参数
    避免过度使用any(),尽可能用eq()或具体值匹配关键参数。
  3. 及时清理过时桩
    使用Mockito.clearInvocations(mock) 而非reset()
  4. 保持测试原子性
    每个测试方法只验证一个明确的业务场景。

通过掌握这些核心API,开发者可以高效构建可靠、易维护的单元测试体系。接下来可进入参数匹配器实战学习更精细的控制技巧。


http://www.kler.cn/a/540804.html

相关文章:

  • 【深度学习】多目标融合算法(四):多门混合专家网络MMOE(Multi-gate Mixture-of-Experts)
  • 嵌入式linux系统中VIM编辑工具用法与GCC参数详解
  • 【C++】命名空间
  • Nginx部署Umi React前端项目标准配置
  • “深入浅出”系列之C++:(18)C++11
  • 【异常解决】在idea中提示 hutool 提示 HttpResponse used withoud try-with-resources statement
  • PriorityQueue优先级队列的使用和Top-k问题
  • 小白零基础学习深度学习之张量
  • 【C++语言】类和对象(下)
  • 备战蓝桥杯:二分算法详解以及模板题
  • Redis持久化机制详解
  • Proxy vs DefineProperty
  • 车载工具报错分析:CANoe、CANalyzer问题:Stuff Error
  • Java 大视界 -- Java 大数据在智能家居中的应用与场景构建(79)
  • Vue:Table合并行于列
  • 用Go实现 SSE 实时推送消息(消息通知)——思悟项目技术4
  • 绘制中国平安股价的交互式 K 线图
  • 31、spark-on-kubernetes中任务报错No space left on device
  • Fastadmin根据链接参数显示不同列表格
  • 10 FastAPI 的自动文档
  • OpenAI 实战进阶教程 - 第十二节 : 多模态任务开发(文本、图像、音频)
  • 持续集成-笔记
  • DeepSeek之于心理学的一点思考
  • Java中有100万个对象,用list map泛型存储和用list对象泛型存储,那个占用空间大,为什么...
  • python两段多线程的例子
  • 网络安全架构分层 网络安全组织架构