SKILL.md
只读
名称
flutter-add-integration-test
描述
配置 Flutter Driver 实现应用交互,并将 MCP 操作转化为持久化的集成测试。适用于在项目中添加集成测试、通过 MCP 探索 UI 组件,或者基于 integration_test 包实现用户操作流程自动化的场景。
实现 Flutter 集成测试
目录
项目配置与依赖添加
配置项目以支持集成测试和 Flutter Driver 扩展。
- 在
pubspec.yaml中添加所需的开发依赖:flutter pub add 'dev:integration_test:{"sdk":"flutter"}' flutter pub add 'dev:flutter_test:{"sdk":"flutter"}' - 在应用入口文件(通常为
lib/main.dart或专门的lib/main_test.dart)中启用 Flutter Driver 扩展:- 导入
package:flutter_driver/driver_extension.dart。 - 在调用
runApp()之前先执行enableFlutterDriverExtension();。
- 导入
- 给应用代码中的关键组件(Widget)添加
Key参数(例如ValueKey('login_button')),确保测试时能精准定位。
通过 MCP 进行交互式探索
在编写静态测试脚本之前,可利用 Dart/Flutter MCP 服务器工具动态探索并操作应用状态。
- 启动应用:执行
launch_app并指定target: "lib/main_test.dart",以启动应用并获取 DTD URI。 - 检查树结构:执行
get_widget_tree查看可用的Key、Text节点以及组件类型(Type)。 - 交互操作:执行
tap、enter_text和scroll来模拟真实用户操作路径。 - 等待同步:在进行页面跳转或触发动画时,务必调用
waitFor或通过get_health校验当前状态。 - 排查未挂载组件:如果组件树中找不到某个 Widget,可能是由于
SliverList或ListView采用了懒加载机制。此时可先执行scroll或scrollIntoView强制其挂载显示,再进行后续交互。
测试编写规范
使用 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断言组件可见性。 - 对于位于屏幕外的组件,使用
await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder);滚动至其可见。
针对旧版 flutter_driver 的兼容处理:
- 如果需要维护或迁移旧版
flutter_driver测试,请使用driver.waitFor()、driver.waitForAbsent()、driver.tap()和driver.scroll(),而非WidgetTesterAPI。
运行测试与性能分析
使用 flutter drive 命令运行测试。这需要一个位于 test_driver/integration_test.dart 的宿主驱动脚本(Host Driver Script),其中调用了 integrationDriver()。
不同目标的执行命令:
- 在 Chrome 上测试: 在独立终端启动
chromedriver --port=4444,然后运行:
flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart -d chrome - 在无头浏览器(Headless Web)上测试: 运行时附带
-d web-server参数。 - 在 Android(本地设备)上测试: 运行
flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart。 - 在 Firebase Test Lab(Android)上测试:
- 打包 Debug APK:
flutter build apk --debug - 打包测试 APK:
./gradlew app:assembleAndroidTest - 将生成的两个 APK 上传至 Firebase Test Lab 控制台。
- 打包 Debug APK:
工作流:端到端集成测试
复制并参照以下清单完成集成测试的实现与验证:
- [ ] 任务进度:环境配置
- [ ] 将
integration_test和flutter_test添加到pubspec.yaml。 - [ ] 在应用入口注入
enableFlutterDriverExtension()。 - [ ] 为目标组件分配
ValueKey。
- [ ] 将
- [ ] 任务进度:交互探索
- [ ] 通过 MCP 运行
launch_app。 - [ ] 使用
get_widget_tree梳理组件树。 - [ ] 利用 MCP 工具(
tap、enter_text)验证交互路径。
- [ ] 通过 MCP 运行
- [ ] 任务进度:代码撰写
- [ ] 创建
integration_test/app_test.dart。 - [ ] 使用
WidgetTesterAPI 编写测试用例。 - [ ] 创建包含
integrationDriver()的test_driver/integration_test.dart。
- [ ] 创建
- [ ] 任务进度:运行与反馈循环
- [ ] 运行
flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart。 - [ ] 反馈循环:检查测试输出 -> 若抛出
PumpAndSettleTimedOutException,检查是否存在无限循环动画 -> 若找不到组件,补充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('End-to-end test', () {
testWidgets('tap on the floating action button, verify counter', (tester) async {
// Load app widget.
await tester.pumpWidget(const MyApp());
// Verify the counter starts at 0.
expect(find.text('0'), findsOneWidget);
// Find the floating action button to tap on.
final fab = find.byKey(const ValueKey('increment'));
// Emulate a tap on the floating action button.
await tester.tap(fab);
// Trigger a frame and wait for animations.
await tester.pumpAndSettle();
// Verify the counter increments by 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,
);
}
},
);
}






