Flutter集成测试Navigator.pop()返回逻辑测试及报错修复
问题原因
你遇到的两个报错和测试不通过的核心原因有三点:
- 路由栈初始化错误:直接将
AddGroupPage设为MaterialApp的home,初始路由栈只有这一个页面,执行Navigator.pop()后会直接退出应用,根本不会回到GroupsPage,后续页面存在性校验必然失败。 - 校验时机错误:点击保存按钮后直接调用
verify,此时路由pop动画还未执行,NavigatorObserver的didPop回调还没触发,校验不到调用行为。 - 类型匹配错误:直接传入无类型的
any匹配器,无法匹配didPop方法要求的Route类型参数;如果没有为Mock类生成正确的重写实现,void返回值的方法会被识别为不可取值的void类型,触发对应报错。
解决方案
1. 修正路由初始化逻辑
将MaterialApp的初始home设为业务主页GroupsPage,测试启动后先模拟用户操作跳转到AddGroupPage,保证路由栈存在两个页面,pop操作才能正常回到主页:
await tester.pumpWidget( BlocProvider( create: (_) => GroupBloc(), child: MaterialApp( home: const GroupsPage(), // 初始主页设为分组列表页 navigatorObservers: [mockObserver], ), ), ); // 模拟点击跳转到添加分组页的按钮,替换为你项目中实际的跳转按钮查找逻辑 await tester.tap(find.byKey(const Key('navigate_to_add_group_btn'))); await tester.pumpAndSettle(); // 可以加一个校验确认已经进入添加页,排除前置流程问题 expect(find.byType(AddGroupPage), findsOneWidget);
2. 替换Mock方案(推荐,无代码生成)
Flutter集成测试官方推荐使用mocktail替代需要代码生成的mockito,能直接解决类型匹配和void方法校验的问题,步骤如下:
- 替换
pubspec.yaml中的测试依赖:
dev_dependencies: flutter_test: sdk: flutter integration_test: sdk: flutter mocktail: ^1.0.3 # 使用当前最新稳定版即可
- 修改测试代码,注册Route类型的fallback值,调整verify时机和写法:
import 'package:mocktail/mocktail.dart'; class MockNavigatorObserver extends Mock implements NavigatorObserver {} void main() { late NavigatorObserver mockObserver; IntegrationTestWidgetsFlutterBinding.ensureInitialized(); setUp(() { mockObserver = MockNavigatorObserver(); // 注册Route类型的兜底值,解决any匹配Route参数的类型错误 registerFallbackValue(MaterialPageRoute(builder: (_) => const SizedBox.shrink())); }); testWidgets( "After inputting a text, go to the display page which contains group that same text ", (WidgetTester tester) async { // 上面写的初始化路由、跳转到添加页的逻辑放在这里 const inputText = 'Group'; await tester.enterText( find.byKey(const Key('add_group_field')), inputText ); final saveGroupBtn = find.byIcon(Icons.save); await tester.tap(saveGroupBtn); // 必须等所有动画、路由操作执行完成后再做校验 await tester.pumpAndSettle(); // 校验didPop调用,写法符合mocktail规范,不会触发void类型错误 verify(() => mockObserver.didPop(any(), any())).called(1); expect(find.byType(AddGroupPage), findsNothing); expect(find.byType(GroupsPage), findsOneWidget); expect(find.text(inputText), findsOneWidget); }, ); }
3. 继续使用mockito的修正方式
如果要保留mockito依赖,需要按以下步骤调整:
- 在测试文件顶部添加Mock生成注解:
@GenerateMocks([NavigatorObserver]) import 'your_test_file.mocks.dart'; // 替换为你实际的生成文件路径
- 执行命令生成Mock代码:
flutter pub run build_runner build --delete-conflicting-outputs
- 调整verify时机和参数类型,将校验逻辑放到
pumpAndSettle之后,为any指定明确的类型:
await tester.tap(saveGroupBtn); await tester.pumpAndSettle(); // 明确指定参数类型,解决类型不匹配问题 verify(mockObserver.didPop(any<Route>(), any<Route?>()));
注意:无论使用哪种Mock库,路由相关的校验必须放在
await tester.pumpAndSettle()之后,否则会因为动画未执行、回调未触发导致校验失败。
内容的提问来源于stack exchange,提问作者userName
相关产品推荐
相关产品推荐

