如何使用Provider结合Mock ViewModel测试Flutter按钮点击方法调用
问题分析与解决方案
你的测试失败核心原因有两个:
- 存根Mock方法的方式错误,针对实例方法的存根需要用lambda表达式包裹调用
- 点击按钮后未等待异步操作完成,导致verify时机过早
修正步骤
1. 修正Mock方法的存根逻辑
如果signIn是UserViewModel的实例方法(而非属性),必须用lambda表达式包裹方法调用才能正确存根:
// 错误写法 // when(mockUVM.signIn).thenAnswer((_) => (Future(() => mUM))); // 正确写法 when(() => mockUVM.signIn()).thenAnswer((_) async => mUM);
2. 点击后等待异步操作与Widget更新
signIn是异步方法,点击按钮后需要等待其执行完成,并让Widget树完成重建:
await tester.tap(find.text('SIGN IN')); // 等待异步操作完成 + Widget树更新 await tester.pumpAndSettle();
3. 确保Mock类正确实现ChangeNotifier
你的MockUserViewModel需要正确mock继承自ChangeNotifier的UserViewModel,示例Mock类:
class MockUserViewModel extends Mock implements UserViewModel {}
(Mocktail会自动处理ChangeNotifier的相关方法,无需额外存根)
修正后的完整测试代码
testWidgets('should call auth function when clicked', (tester) async { final mockUVM = MockUserViewModel(); final mUM = UserModel(); // 修正存根方式 when(() => mockUVM.signIn()).thenAnswer((_) async => mUM); await tester.pumpWidget( ChangeNotifierProvider.value( value: mockUVM, builder: (context, child) { return const MaterialApp(home: ScreenLogin()); }, ), ); await tester.tap(find.text('SIGN IN')); // 等待异步操作完成 await tester.pumpAndSettle(); // 验证方法调用 verify(() => mockUVM.signIn()).called(greaterThan(0)); });
额外检查点
- 确认
ScreenLogin内部没有再创建UserViewModel的Provider实例(比如在build方法里用ChangeNotifierProvider(create: ...)),否则会覆盖测试中传入的mock实例 - 确认
Provider.of<UserViewModel>(context, listen: false)的泛型类型和mock的类型完全匹配,无拼写错误
内容的提问来源于stack exchange,提问作者tooba
相关产品推荐
相关产品推荐

