掌握 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 方法的返回值。
使用场景:
- 当返回值是一个
Future
或Stream
时(这是最常见的用法)。 - 当返回值需要根据传入的参数动态计算时。
- 当您想在方法被调用时执行一些额外的逻辑(比如打印日志)。
代码示例:
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 配合使用,用于临时恢复真实行为 |
掌握这四种核心的"打桩"规则,您就能够非常自如地为几乎所有场景编写出简洁、健壮的单元测试了。