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

Flutter使用Provider绑定ViewModel时如何测试页面导航逻辑

解决方案

核心问题说明

你当前测试代码的主要问题有2个:

  • MainMenuScreen没有接收ViewModel的构造参数,你是通过Provider.of在组件内部获取实例,所以不能直接通过构造函数传入Mock实例,需要在上层用ChangeNotifierProvider包裹来提供Mock的ViewModel
  • 测试环境缺少本地化配置,直接构建MaterialApp会导致AppLocalizations.of(context)返回空,无法正常渲染文本

完整测试代码

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:provider/provider.dart';
// 导入你自己的项目相关类
import 'package:your_app_name/main.dart';
import 'package:your_app_name/main_menu_screen.dart';
import 'package:your_app_name/about_screen.dart';

class MockMainMenuViewModel extends Mock implements MainMenuViewModel {}
class MockNavigatorObserver extends Mock implements NavigatorObserver {}

void main() {
  group('MainMenuScreen导航测试', () {
    late MockNavigatorObserver mockObserver;
    late MockMainMenuViewModel mockViewModel;

    setUp(() {
      mockObserver = MockNavigatorObserver();
      mockViewModel = MockMainMenuViewModel();
    });

    Future<void> buildTestApp(WidgetTester tester) async {
      await tester.pumpWidget(
        MultiProvider(
          providers: [
            // 提供Mock的ViewModel
            ChangeNotifierProvider<MainMenuViewModel>.value(value: mockViewModel),
            // 如果其他ViewModel没有用到可以只提供需要的,或者也Mock之后加进来
            ChangeNotifierProvider(create: (context) => LevelSelectionViewModel()),
            ChangeNotifierProvider(create: (context) => LevelViewModel()),
          ],
          child: MaterialApp(
            localizationsDelegates: const [
              AppLocalizations.delegate,
              GlobalMaterialLocalizations.delegate,
              GlobalWidgetsLocalizations.delegate,
              GlobalCupertinoLocalizations.delegate,
            ],
            supportedLocales: const [
              Locale('en', ''),
              Locale('es', ''),
            ],
            // 注入模拟的导航观察者
            navigatorObservers: [mockObserver],
            home: const MainMenuScreen(),
          ),
        ),
      );
      // 等待页面渲染完成
      await tester.pumpAndSettle();
    }

    testWidgets('点击About按钮跳转到关于页面', (WidgetTester tester) async {
      await buildTestApp(tester);
      // 验证About按钮存在
      final aboutBtn = find.text('About');
      expect(aboutBtn, findsOneWidget);

      // 点击按钮
      await tester.tap(aboutBtn);
      await tester.pumpAndSettle();

      // 方式1:直接验证AboutScreen是否已渲染
      expect(find.byType(AboutScreen), findsOneWidget);

      // 方式2:通过导航观察者验证导航事件是否触发(可选)
      // verify(mockObserver.didPush(any, any));
    });
  });
}

额外优化建议

你当前main.dart里存在重复注册MainMenuViewModel的问题:外层MultiProvider已经注册了一次,MaterialApp的home属性里又单独注册了一次,多余的注册可以删掉,修改如下:

// 原来的home部分
home: const MainMenuScreen(),

这样就不会重复创建ViewModel实例了。

后续切换为Key识别的修改点

等你给About按钮加上Key之后,只需要把查找按钮的代码改为按Key查找即可:

final aboutBtn = find.byKey(const Key('btn_about'));

内容的提问来源于stack exchange,提问作者greysqrl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 23:54:06