Flutter中如何用Mockito正确模拟并验证返回Future的函数?
问题:模拟返回Future的函数时出现类型错误
我尝试模拟一个返回Future<bool>且接收String类型参数的函数,编写了如下测试代码:
final Future<bool> Function(String) _handleFilterSelect = MockFunction(); when(_handleFilterSelect.call('/view/movie_1234')) .thenAnswer((_) => Future.value(true)); FilterList filterList = new FilterList( '/view/movie', onFilterSelect: _handleFilterSelect, ); await widgetTester.pumpWidget(MaterialApp( localizationsDelegates: delegate, home: Scaffold( body: filterList, ), theme: ThemeUtil().getCurrentTheme()!.defaultThemeData, )); await widgetTester.pumpAndSettle(); final filtersFinder = find.byType(ListTile); await widgetTester.tap(filtersFinder); await widgetTester.pumpAndSettle(); verify(_handleFilterSelect('/view/movie_1234')).called(1); // 测试外部的代码 class MockFunction extends Mock { Future<bool> call(String param); }
运行测试时抛出如下错误:
The following _TypeError was thrown running a test: type 'Null' is not a subtype of type 'Future'
当该函数为非Future返回类型时测试可正常运行,请问正确实现返回Future类型的_handleFilterSelect的方式是什么?
解决方案
这个错误的核心原因是:Mock类默认会返回null,当你的Stub(即when语句)没有匹配到实际调用时,就会返回null,而组件期望的是Future<bool>类型,因此触发类型转换错误。结合你的场景,有两种可靠的修复方式:
方式一:修正Stub的匹配方式
Dart中调用函数func(param)等价于func.call(param),但Mockito对这两种写法的匹配逻辑可能存在差异。直接Stub函数调用而非call方法,能确保匹配更准确:
// 替换原有的when语句 when(_handleFilterSelect('/view/movie_1234')) .thenAnswer((_) => Future.value(true));
同时保持MockFunction的定义不变即可。
方式二:使用typedef定义函数类型,让Mock类实现该类型
通过typedef明确函数签名,能让Mockito更好地识别函数类型,避免匹配问题:
- 先定义函数类型的typedef:
typedef FilterSelectCallback = Future<bool> Function(String);
- 修改Mock类实现该typedef:
class MockFilterSelectCallback extends Mock implements FilterSelectCallback {}
- 在测试中使用这个Mock类:
final _handleFilterSelect = MockFilterSelectCallback(); // Stub函数调用 when(_handleFilterSelect('/view/movie_1234')) .thenAnswer((_) => Future.value(true)); // 后续的组件初始化、测试逻辑保持不变 FilterList filterList = FilterList( '/view/movie', onFilterSelect: _handleFilterSelect, ); // ... 其余测试代码
额外注意事项
- 确保组件调用
onFilterSelect时传入的参数和你Stub的参数完全一致(比如路径是否带后缀、大小写是否匹配),否则Stub无法触发,依然会返回null。 - 调用
verify时,同样使用_handleFilterSelect('/view/movie_1234')而非call方法,保持和Stub的写法一致。
内容的提问来源于stack exchange,提问作者Vishnu
相关产品推荐
相关产品推荐

