flutter-add-integration-test

flutter-add-integration-test

热门

配置 Flutter Driver 以实现应用交互,并将 MCP 操作转换为永久性集成测试。适用于为项目添加集成测试、通过 MCP 探索 UI 组件,或使用 integration_test 包自动化用户流程。

2525Star
152Fork
更新于 2026/6/18
SKILL.md
只读
名称
flutter-add-integration-test
描述

配置 Flutter Driver 以实现应用交互,并将 MCP 操作转换为永久性集成测试。适用于为项目添加集成测试、通过 MCP 探索 UI 组件,或使用 integration_test 包自动化用户流程。

实现 Flutter 集成测试

目录

项目设置与依赖

配置项目以支持集成测试和 Flutter Driver 扩展。

  1. pubspec.yaml 中添加所需的开发依赖:
    flutter pub add 'dev:integration_test:{"sdk":"flutter"}'
    flutter pub add 'dev:flutter_test:{"sdk":"flutter"}'
    
  2. 在应用入口点(通常是 lib/main.dart 或专用的 lib/main_test.dart)中启用 Flutter Driver 扩展:
    • 导入 package:flutter_driver/driver_extension.dart
    • runApp() 之前调用 enableFlutterDriverExtension();
  3. 为应用代码中的关键 widget 添加 Key 参数(例如 ValueKey('login_button')),以确保测试中能够可靠定位。

通过 MCP 进行交互式探索

使用 Dart/Flutter MCP 服务器工具,在编写静态测试之前交互式地探索和操作应用状态。

  • 启动:执行 launch_app,参数为 target: "lib/main_test.dart",以启动应用并获取 DTD URI。
  • 检查:执行 get_widget_tree 以发现可用的 KeyText 节点和 widget Type
  • 交互:执行 tapenter_textscroll 来模拟用户流程。
  • 等待:在导航或触发动画时,始终执行 waitFor 或通过 get_health 验证状态。
  • 排查未挂载的 Widget:如果 widget 在树中未找到,可能是由于懒加载在 SliverListListView 中。执行 scrollscrollIntoView 强制 widget 挂载后再进行交互。

测试编写指南

使用 flutter_test API 范式来组织集成测试。

  • 在项目根目录创建专用的 integration_test/ 目录。
  • 所有测试文件使用 <name>_test.dart 命名约定。
  • main() 开头调用 IntegrationTestWidgetsFlutterBinding.ensureInitialized(); 初始化绑定。
  • 使用 await tester.pumpWidget(MyApp()); 加载应用 UI。
  • tester.tap() 等交互后,使用 await tester.pumpAndSettle(); 触发帧并等待动画完成。
  • 使用 expect(find.byKey(ValueKey('foo')), findsOneWidget);findsNothing 断言 widget 可见性。
  • 使用 await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder); 滚动到屏幕外的特定 widget。

针对旧版 flutter_driver 的条件逻辑:

  • 如果维护或迁移旧版 flutter_driver 测试,请使用 driver.waitFor()driver.waitForAbsent()driver.tap()driver.scroll() 代替 WidgetTester API。

执行与性能分析

使用 flutter drive 命令执行测试。需要位于 test_driver/integration_test.dart 的主机驱动脚本,该脚本调用 integrationDriver()

条件执行目标:

  • 如果在 Chrome 上测试: 在单独的终端中启动 chromedriver --port=4444,然后运行:
    flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart -d chrome
  • 如果进行无头 Web 测试: 使用 -d web-server 运行。
  • 如果在 Android(本地)上测试: 运行 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart
  • 如果在 Firebase Test Lab(Android)上测试:
    1. 构建调试 APK:flutter build apk --debug
    2. 构建测试 APK:./gradlew app:assembleAndroidTest
    3. 将两个 APK 上传到 Firebase Test Lab 控制台。

工作流:端到端集成测试

复制并遵循此清单以实现和验证集成测试。

  • [ ] 任务进度:设置
    • [ ] 将 integration_testflutter_test 添加到 pubspec.yaml
    • [ ] 在应用入口点注入 enableFlutterDriverExtension()
    • [ ] 为目标 widget 分配 ValueKey
  • [ ] 任务进度:探索
    • [ ] 通过 MCP 运行 launch_app
    • [ ] 使用 get_widget_tree 映射 widget 树。
    • [ ] 使用 MCP 工具(tapenter_text)验证交互路径。
  • [ ] 任务进度:编写
    • [ ] 创建 integration_test/app_test.dart
    • [ ] 使用 WidgetTester API 编写测试用例。
    • [ ] 创建包含 integrationDriver()test_driver/integration_test.dart
  • [ ] 任务进度:执行与反馈循环
    • [ ] 运行 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart
    • [ ] 反馈循环:检查测试输出 -> 如果出现 PumpAndSettleTimedOutException,检查是否存在无限动画 -> 如果 widget 未找到,添加 scrollUntilVisible -> 重新运行测试直至通过。

示例

标准集成测试(integration_test/app_test.dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('端到端测试', () {
    testWidgets('点击浮动操作按钮,验证计数器', (tester) async {
      // 加载应用 widget。
      await tester.pumpWidget(const MyApp());

      // 验证计数器初始值为 0。
      expect(find.text('0'), findsOneWidget);

      // 找到要点击的浮动操作按钮。
      final fab = find.byKey(const ValueKey('increment'));

      // 模拟点击浮动操作按钮。
      await tester.tap(fab);

      // 触发帧并等待动画。
      await tester.pumpAndSettle();

      // 验证计数器增加了 1。
      expect(find.text('1'), findsOneWidget);
    });
  });
}

主机驱动脚本(test_driver/integration_test.dart

import 'package:integration_test/integration_test_driver.dart';

Future<void> main() => integrationDriver();

性能分析驱动脚本(test_driver/perf_driver.dart

如果测试操作包裹在 binding.traceAction() 中以捕获性能指标,请使用此驱动脚本。

import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart';

Future<void> main() {
  return integrationDriver(
    responseDataCallback: (data) async {
      if (data != null) {
        final timeline = driver.Timeline.fromJson(
          data['scrolling_timeline'] as Map<String, dynamic>,
        );

        final summary = driver.TimelineSummary.summarize(timeline);

        await summary.writeTimelineToFile(
          'scrolling_timeline',
          pretty: true,
          includeSummary: true,
        );
      }
    },
  );
}