Dart测试中如何等待未被测试代码直接调用的Future执行完成
解决方案
方案1:调整待测试方法签名(最推荐)
你的methodToTest本身存在异步副作用,直接将其改为返回Future是更合理的设计,同时也能解决测试等待的问题,还能避免异步错误被静默吞掉:
String apiResult = 'foo'; // 调整后签名 Future<void> methodToTest() async { apiResult = await apiCall(); }
测试时直接await即可:
test('methodToTest works', () async { expect(apiResult, equals('foo')); await methodToTest(); expect(apiResult, equals('bar')); });
方案2:无法修改业务代码时,等待队列任务执行完成
如果不能修改methodToTest的签名,可以等待Dart的微任务队列和事件队列全部处理完成,比原来的Future.delayed(Duration.zero)更可靠:
test('methodToTest works', () async { expect(apiResult, equals('foo')); methodToTest(); // 等待所有微任务、事件队列中的任务处理完成 await Future(() {}); expect(apiResult, equals('bar')); });
如果apiCall内部存在多层嵌套异步、或者Mock模拟了网络延迟,上面的方式仍不生效,可以加轮询判断逻辑:
test('methodToTest works', () async { expect(apiResult, equals('foo')); methodToTest(); // 最多等待1秒,每10ms检查一次结果是否符合预期 final timeout = DateTime.now().add(const Duration(seconds: 1)); while (DateTime.now().isBefore(timeout) && apiResult != 'bar') { await Future.delayed(const Duration(milliseconds: 10)); } expect(apiResult, equals('bar')); });
方案3:Flutter测试场景直接用pumpAndSettle
如果是Flutter项目的widget测试,直接调用tester.pumpAndSettle即可自动等待所有异步任务执行完成:
testWidgets('methodToTest works', (tester) async { expect(apiResult, equals('foo')); methodToTest(); await tester.pumpAndSettle(); expect(apiResult, equals('bar')); });
之前
Future.delayed(Duration.zero)失效的原因是:它只会等待事件队列的最靠前任务执行,如果你的api调用内部有多层异步回调、或者Mock模拟了延迟,等待时长不够就会提前执行后续断言。
内容的提问来源于stack exchange,提问作者Hannes Hultergård
相关产品推荐
相关产品推荐

