ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Flutter 仓库 flutter_tools 非封闭式集成测试指南:integration.shard 的运行原理与最佳实践

Flutter 仓库 flutter_tools 非封闭式集成测试指南:integration.shard 的运行原理与最佳实践 Flutter 仓库 flutter_tools 非封闭式集成测试指南integration.shard 的运行原理与最佳实践【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutterflutter命令行工具flutter_tools除了大量的单元测试还维护着一套专门用于验证「工具真实运行行为」的集成测试套件即 packages/flutter_tools/test/integration.shard。本指南围绕该目录的官方说明展开讲解它为何「非封闭not hermetic」、如何在本地下载好 Dart SDK 后用一行命令跑通全部测试、为何它在 CI 上被单独分片并排除在覆盖率统计之外以及新增测试文件必须遵循的命名约定并在此基础上结合仓库中的驱动基础设施与真实测试用例还原这套测试从「启动 flutter 子进程」到「通过 VM Service 断言行为」的完整链路。一、什么是 integration.shard黑盒地驱动真实的 flutter 工具在 Flutter 主仓库中flutter工具自身的测试被明确分成若干层。packages/flutter_tools/README.mdWriting tests 一节给出了官方划分测试目录定位test/general.shard工具内部实现的封闭式hermetic单元测试单条必须远小于 2 秒运行完成test/commands.shard针对工具命令的测试其下再分hermetic/与permeable/子目录test/integration.shard集成测试以子进程方式真实运行 flutter 工具test/web.shard运行较慢的 Web 相关测试而 integration.shard 的 README 在开头就点明了这套测试的两个关键特征These tests are not hermetic, and use the actual Flutter SDK. While they dont require actual devices, they runflutter_testerto test Dart VM and Flutter integration.非封闭not hermetic它们不复用工具内部的 Dart 对象而是把flutter作为一个真实的可执行文件以子进程方式拉起black-box 行为使用真实的 Flutter SDK 环境不需要真实设备虽然不连手机/模拟器但它们借助flutter_testerflutter 引擎内置的桌面宿主来验证 Dart VM 与 Flutter 框架之间的真实集成。「为何必须这样测试」可以从源码侧印证集成测试中真正要验证的经常是进程生命周期、daemon 协议消息、hot reload 消息往返、构建产物等只有跑完整工具才能观察到的行为单元测试无法覆盖。二、本地运行全套集成测试2.1 官方给出的运行命令按 integration.shard/README.md 的说明进入flutter_tools目录后执行../../bin/cache/dart-sdk/bin/dart test test/integration.shard注意几个细节命令中的../../bin/cache/dart-sdk是相对packages/flutter_tools目录定位仓库缓存中的 Dart SDK该 shard 依赖的是 Flutter 工具测试自身的package:test配置。在 packages/flutter_tools/dart_test.yaml 中全局超时被放大到 15 分钟理由注释写得很清楚部分测试耗时极长且宿主机过载时会进一步拖慢因此「测试内部永不自行设置超时」。2.2 前置条件先让 Flutter 下载 Dart SDKREADME 强调必须在 Flutter clone 中已经下载好 Dart SDK命令才能工作。触发方式很简单——先运行一次仓库根下的 flutter 即可../../bin/flutter --version运行../../bin/flutterwrapper 脚本会自动完成 Dart SDK 与引擎工件的下载随后上面的dart test才能解析执行。2.3 定位 flutter 根目录FLUTTER_ROOT 的作用集成测试的驱动代码需要知道 flutter 仓库根目录在哪里。packages/flutter_tools/test/integration.shard/test_utils.dart 中真实待执行的flutter二进制路径即拼接自仓库根final String flutterBin fileSystem.path.join( getFlutterRoot(), bin, platform.isWindows ? flutter.bat : flutter, );而getFlutterRoot()定义于 packages/flutter_tools/test/src/common.dart它优先读取FLUTTER_ROOT环境变量若未设置则从platform.script反推flutter_tools所在路径。因此当脚本方式无法定位时可以像 packages/flutter_tools/README.md 提示的那样显式导出export FLUTTER_ROOT~/path/to/flutter-sdk flutter test test/integration.shard --concurrency 1由于这套集成测试比general.shard慢很多官方建议在本地开发机上用--concurrency 1串行执行README 同时提醒完整跑完可能耗时约一小时量级多数场景更适合交给 CI或只手工验证你正在改动的那部分行为。2.4 只跑单个用例/文件跑单个文件或单个用例的方式与普通package:test一致例如 packages/flutter_tools/README.md 展示了配合本地引擎跑单个用例的命令export FLUTTER_LOCAL_ENGINEandroid_debug_unopt export FLUTTER_LOCAL_ENGINE_HOSThost_debug_unopt flutter test test/integration.shard/some_test_case也可直接用 Dart SDK 指定文件路径执行例如只跑 hot reload 相关测试../../bin/cache/dart-sdk/bin/dart test test/integration.shard/hot_reload_test.dart2.5 进阶使用本地编译的引擎当你在同时开发 flutter 引擎时集成测试可以通过环境变量切换到本地引擎构建产物。test_utils.dart中定义了三个相关环境变量并拼装成 CLI 参数环境变量含义FLUTTER_LOCAL_ENGINE本地引擎变体名如android_debug_unopt映射为--local-engineFLUTTER_LOCAL_ENGINE_HOST本地宿主引擎名如host_debug_unopt映射为--local-engine-hostFLUTTER_LOCAL_ENGINE_SRC_PATH引擎源码路径映射为--local-engine-src-path当 flutter 与 engine 检出在相邻目录时通常无需设置对应实现见 test_utils.dart 的getLocalEngineArguments()这些参数会被拼接进每个flutter run/flutter test/flutter attach子进程的启动参数中。三、底层驱动机制FlutterTestDriver 与 flutter-tester 设备要理解「这些测试如何工作」最值得读的是 packages/flutter_tools/test/integration.shard/test_driver.dart。它定义了抽象基类FlutterTestDriver负责以 test_utils.dart 计算出的flutterBin为入口通过LocalProcessManager启动真实子进程工作目录为测试动态创建的临时项目目录并注入FLUTTER_TESTtrue、FLUTTER_WEBtrue环境变量见_setupProcess逐行转发子进程的 stdout/stderr捕获 stderr 到错误缓冲以便断言失败时输出完整上下文解析 flutter 工具在--machineJSON 协议模式下的输出行parseFlutterResponse并等待daemon.connected、app.start、app.started、app.debugPort等关键事件通过vm_service包连接 VM Service订阅 isolate/debug/service 事件流注册reloadSources、hotRestart、flutterVersion等服务扩展的监听提供resume/stepOver/stepInto/stepOut、下断点breakAt/addBreakpoint、表达式求值、读取调用栈等调试原语优雅退出先向记录到的真实 PID 发送 SIGTERM超时后升级为 SIGKILL并处理 Windows 下 flutter.bat 是 shell 脚本导致_process本身是 shell 进程的特殊情况。其下派生两类具体驱动FlutterRunTestDriver执行flutter run/flutter attach并封装hotReload、hotRestart、scheduleFrame调用ext.ui.window.scheduleFrame、stop/detach等操作FlutterTestTestDriver执行flutter test解析 JSON 输出中的{success:true,type:done,...}判定测试结束。默认目标设备是flutter-tester源码中来自FlutterTesterDevices.kTesterDeviceId这正是 README 所说「不需要真实设备」的技术基础需要 Web 场景时也可切换为GoogleChromeDevice.kChromeDeviceIdheadless Chrome或 WebServer 设备。四、典型的被测场景与代表性用例integration.shard 下目前按主题存放了大量*_test.dart文件其下还有debug_adapter/、isolated/、test_data/等子目录粗略可分为几类运行与热重载如 flutter_run_test.dart、hot_reload_test.dart、hot_reload_errors_test.dart、hot_restart_with_unhandled_exception_test.dart、background_isolate_test.dart调试器与 VM Servicedebugger_stepping_test.dart、expression_evaluation_test.dart、break_on_framework_exceptions_test.dart、timeline_test.dart以及debug_adapter/子目录下的 DAPDebug Adapter Protocol相关测试多平台构建Android如 flutter_build_apk_split_per_abi_test.dart、android_obfuscate_test.dart、iOS/macOSbuild_ios_config_only_test.dart、macos_assemble_test.dart、Webweb_define_build_test.dart、flutter_build_wasm_test.dart、Windows/Linuxbuild_windows_config_only_test.dart、build_linux_config_only_test.dartSwift Package Manager 与 Gradle 插件swift_package_manager_test.dart 与 android_run_flutter_gradle_plugin_tests_test.dart后者同时驱动 packages/flutter_tools/gradle 的构建测试gen_l10n、deferred components、widget_preview 等专项如 gen_l10n_test.dart、deferred_components_test.dart、widget_preview_smoke_test.dart注意源码中多次出现 WARNING: this log message is used by test/integration.shard/... 注释说明这些用例会断言工具在特定场景输出的日志内容。以 flutter_run_test.dart 为最小示例可以看清整条「模板」setUp中先在临时目录里创建真实 Flutter 工程并执行flutter pub get再new一个FlutterRunTestDrivertearDown中调用flutter.stop()优雅收尾并删除临时目录用例则断言flutter run -d invalid-device-id的错误输出、flutter run是否输出 DTD/DevTools 事件、app.start事件中的 deviceId/mode 等。真实项目内容来自 test_data 中以字符串形式内嵌 pubspec 与lib/main.dart的 fixture基类 project.dart 的setUpIn会一次性写出 pubspec、main.dart、test.dart、web/index.html、flutter.js 等文件并调用getPackages。五、为什么这些测试被排除出覆盖率统计README 的 Coverage exclusion 一节integration.shard/README.md给出了明确的工程决策与理由这些测试运行成本很高由于它们是黑盒测试——把 flutter 工具作为子进程来跑而不是直接调用其内部函数——无法给出对flutter工具有意义的覆盖率信息覆盖率工具无法看到子进程内部执行了哪些工具代码行因此它们在 CI 上被放进独立的分片separate shard并且不参与覆盖率计算。这解释了两个现象其一覆盖率的计算只面向general.shard等单元测试其二这些集成测试的「价值」不在于测出覆盖率而在于守护真实 CLI 行为与跨进程协议的正确性属于功能验证而非度量手段。六、新增集成测试文件的硬性约定README 末尾integration.shard/README.md强调When adding a new test file make sure that it ends with_test.dart, or else it will not be run.即新增测试文件必须以_test.dart结尾否则不会被测试框架识别与执行。这是 dart 官方test包对入口文件名的默认要求integration.shard 内的实现也遵循同一规则。除命名外从现有代码还能总结出几条「软约定」供参考大量文件会在库级声明Tags(String[flutter-test-driver])如 flutter_run_test.dart。对应的标签在 packages/flutter_tools/dart_test.yaml 中登记flutter-test-driver表示会调用flutter test/flutter runflutter-build-apk表示会执行flutter build apkCI 可按标签分类调度文件里通常import ../src/common.dart提供testWithoutContext、getFlutterRoot等共享工具以及test_data/下的 fixture 与test_driver.dart、test_utils.dart不自行设置超时全部依赖dart_test.yaml的 15 分钟全局超时属于 Android 预览版 SDK / Java 17 等专项场景的测试会放至 packages/flutter_tools/test/android_preview_integration.shard 等同族目录但其 README 明确说明它们本质上也复用../integration.shard的共享工具test_utils.dart——这也从侧面说明integration.shard是整个 flutter_tools 非封闭测试体系的公共底座。七、小结与本地实践建议围绕 integration.shard/README.md 这则「看似简短」的说明可以梳理出一套完整的认知模型本质把 flutter 工具当黑盒真实启动用flutter_tester在无真机条件下验证 Dart VM 与 Flutter 的端到端集成运行先../../bin/flutter --version触发 Dart SDK 下载再从packages/flutter_tools执行../../bin/cache/dart-sdk/bin/dart test test/integration.shard定位失败时通过FLUTTER_ROOT显式指定仓库根本机建议--concurrency 1串行以控制负载与并发冲突前置条件可用FLUTTER_LOCAL_ENGINE/FLUTTER_LOCAL_ENGINE_HOST/FLUTTER_LOCAL_ENGINE_SRC_PATH切换到自编译引擎CI 定位单独分片运行、不计覆盖率属于高成本的黑盒功能验证扩展规则新文件必须_test.dart结尾并建议声明flutter-test-driver/flutter-build-apk标签、复用test_data/工程夹具与FlutterRunTestDriver等基础设施。如果你正为 flutter_tools 贡献代码、改动涉及run/test/attach/build等命令的真实行为或想理解 hot reload、调试协议、构建流程如何在真实工具进程中被验证integration.shard 是比单元测试更贴近用户真实操作的观察窗口。【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表