`mockito` 的核心“打桩”规则

掌握 mockito 中不同的"打桩 (Stubbing)"技巧,是写出高质量单元测试的关键。

when(...).thenAnswer(...) 只是其中一种方式,实际上 mockito 提供了一套非常灵活的 API 来规定 Mock 对象在被调用时的行为。

总结一下最常用、最重要的几种规则:


mockito 的核心"打桩"规则

假设我们有一个 mockRepository,它有一个方法 String? findUser(int id)

1. thenReturn - 返回一个具体的值

这是最常用、最简单的规则。它规定当方法被调用时,直接返回一个预设好的值。

使用场景 :当方法的返回值是一个可以直接创建的、简单的对象时(如 String, int, bool,或者一个你自己创建的模型实例)。

代码示例

dart 复制代码
// 规定:当 findUser 被以参数 1 调用时,返回字符串 '张三'
when(mockRepository.findUser(1)).thenReturn('张三');

// 规定:当 findUser 被以任何整数参数调用时,返回字符串 '默认用户'
// an_y 是 mockito 提供的匹配器,表示"任何值"
when(mockRepository.findUser(any)).thenReturn('默认用户');

// 规定:当一个返回 Future 的方法被调用时,返回一个已完成的 Future
when(mockRepository.syncFromRemote()).thenReturn(Future.value()); 

2. thenThrow - 抛出一个异常

用于测试代码中的错误处理逻辑。

使用场景:模拟网络请求失败、数据库读取错误、文件未找到等异常情况。

代码示例

dart 复制代码
// 规定:当 findUser 被以参数 -1 调用时,抛出一个 Exception
when(mockRepository.findUser(-1)).thenThrow(Exception('用户ID无效'));

// 测试代码就可以这样写:
expect(
  () => userManager.getUserName(-1), 
  throwsException
);

3. thenAnswer - 执行一个函数并返回其结果

这是最强大、最灵活 的规则。它允许你提供一个函数,当 Mock 方法被调用时,mockito 会执行你提供的这个函数,并将函数的返回值作为 Mock 方法的返回值。

使用场景

  • 当返回值是一个 FutureStream 时(这是最常见的用法)。
  • 当返回值需要根据传入的参数动态计算时。
  • 当您想在方法被调用时执行一些额外的逻辑(比如打印日志)。

代码示例

dart 复制代码
// 规定:当 syncFromRemote 被调用时,执行一个异步函数,并返回一个空的 Future
// 这是您问题中的例子,非常适合异步方法
when(mockRepository.syncFromRemote()).thenAnswer((_) async => {});

// 规定:当 findUser 被调用时,根据传入的 id 动态返回一个名字
when(mockRepository.findUser(any)).thenAnswer((invocation) {
  // invocation 对象包含了调用的所有信息,包括参数
  final int id = invocation.positionalArguments.first;
  return '用户 $id';
});

// 使用
print(mockRepository.findUser(101)); // 会输出: '用户 101'

4. thenCallRealMethod - 调用真实的原始方法

使用场景 :这在使用 spy 时非常有用。spy 是一种特殊的 Mock 对象,它会"包装"一个真实的对象。默认情况下,调用 spy 的方法会执行真实的方法,但您可以用 when 来覆盖其中某几个方法的行为。thenCallRealMethod 可以让被覆盖的方法恢复其原始的真实行为。

代码示例

dart 复制代码
// 1. 创建一个真实的对象和一个 spy
final realRepo = RealRepository();
final spyRepo = spy(realRepo);

// 2. 默认情况下,spy 会调用真实方法
spyRepo.someMethod(); // 执行 RealRepository.someMethod()

// 3. 我们可以覆盖某个方法的行为
when(spyRepo.someMethod()).thenReturn('假的返回值');
spyRepo.someMethod(); // 返回 '假的返回值'

// 4. 现在,我们可以让它恢复真实的行为
when(spyRepo.someMethod()).thenCallRealMethod();
spyRepo.someMethod(); // 再次执行 RealRepository.someMethod()

总结

规则 作用 最常用场景
thenReturn(value) 直接返回一个预设的值 同步方法,返回简单对象
thenThrow(error) 直接抛出一个预设的异常 测试错误处理和异常流程
thenAnswer(function) 执行一个函数并返回其结果 异步方法 (Future),或需要根据参数动态返回值的场景
thenCallRealMethod() 调用被包装的真实方法 spy 配合使用,用于临时恢复真实行为

掌握这四种核心的"打桩"规则,您就能够非常自如地为几乎所有场景编写出简洁、健壮的单元测试了。

相关推荐
消失的旧时光-19432 小时前
Flutter 异步 + 状态管理融合实践:Riverpod 与 Bloc 双方案解析
flutter
程序员老刘4 小时前
Flutter版本选择指南:避坑3.27,3.35基本稳定 | 2025年10月
flutter·客户端
—Qeyser10 小时前
Flutter网络请求Dio封装实战
网络·flutter·php·xcode·android-studio
消失的旧时光-194312 小时前
Flutter 响应式 + Clean Architecture / MVU 模式 实战指南
android·flutter·架构
你听得到1112 小时前
卷不动了?我写了一个 Flutter 全链路监控 SDK,从卡顿、崩溃到性能,一次性搞定!
前端·flutter·性能优化
YUFENGSHI.LJ12 小时前
Flutter 高性能 Tab 导航:懒加载与状态保持的最佳实践
开发语言·flutter·1024程序员节
__WanG1 天前
如何编写标准StatefulWidget页面
前端·flutter
LinXunFeng1 天前
Flutter 多仓库本地 Monorepo 方案与体验优化
前端·flutter·架构
2501_919749031 天前
flutter鸿蒙:实现类似B站或抖音的弹幕功能
flutter·华为·harmonyos