You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 10:41:33