尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Flutter Framework 泄漏跟踪实战:Leak Tracking 的原理、配置与 CI 集成

Flutter Framework 泄漏跟踪实战:Leak Tracking 的原理、配置与 CI 集成 Flutter Framework 泄漏跟踪实战Leak Tracking 的原理、配置与 CI 集成【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter本篇技术指南围绕 Flutter 仓库官方的泄漏跟踪文档docs/contributing/testing/Leak-tracking.md展开系统讲解如何在flutter test中通过--dart-define LEAK_TRACKINGtrue启用 leak_tracker 对未释放对象如FocusNode、TextEditingController等 disposable的检测能力。读完本文你将掌握如何在本地一条命令开启泄漏跟踪、如何阅读泄漏失败的输出报告、泄漏跟踪在 flutter_test_config.dart 中的完整配置链路、testWidgets的experimentalLeakTesting参数如何为单个测试或整个文件做豁免以及 Flutter CI 中泄漏跟踪分片shard在 .ci.yaml 里的落地方式。快速开始开启本地泄漏跟踪按照官方文档的 TL;DR在本地为 widget 测试启用泄漏跟踪只需要一条命令flutter test --dart-define LEAK_TRACKINGtrue除了编译期注入--dart-define之外也可以设置运行时环境变量export LEAK_TRACKINGtrue两种方式的区别在配置文件的源码中体现得非常清楚。packages/flutter/test/flutter_test_config.dart 中的判定函数同时读取了两个来源/// If true, leak tracking is enabled for all testWidgets. /// /// By default it is false. /// To enable the leak tracking, either pass the compilation flag /// --dart-define LEAK_TRACKINGtrue or invoke export LEAK_TRACKINGtrue. bool _isLeakTrackingEnabled() { if (kIsWeb) { return false; } // The values can be different, one is compile time, another is run time. return const bool.fromEnvironment(LEAK_TRACKING) || (bool.tryParse(Platform.environment[LEAK_TRACKING] ?? ) ?? false); }见 flutter_test_config.dart 的说明const bool.fromEnvironment(LEAK_TRACKING)对应编译期注入的--dart-define LEAK_TRACKINGtruePlatform.environment[LEAK_TRACKING]对应运行时的环境变量export LEAK_TRACKINGtrue两者取“或”任意一种方式命中即视为开启Web 平台kIsWeb下泄漏跟踪强制关闭从源码结构看这与 leak_tracker 在 Web 运行时暂不支持完整跟踪有关框架侧通过LeakTracking.warnForUnsupportedPlatforms false抑制了对不支持平台的警告。泄漏失败长什么样读懂错误报告Flutter Framework 的 widget 测试使用leak_tracker包检测“创建后未被 dispose 的对象”。当一个测试泄漏了可释放对象时测试会以断言失败的形式终止输出形如Expected: leak free Actual: Instance of Leaks Which: contains leaks: # The text is generated by leak_tracker. # For leak troubleshooting tips open: # https://github.com/flutter/flutter/blob/main/docs/contributing/testing/Leak-tracking.md notDisposed: total: 1 objects: FocusNode: test: Align smoke test identityHashCode: 82308154这份报告的几个关键字段值得逐个理解notDisposed泄漏类别为“创建后没有调用dispose()”。报告中每一段注释都是 leak_tracker 自动生成的其中专门指向本文档即 Leak-tracking.md作为排障入口——这不是巧合框架在配置里显式把排障文档链接改写成了这个地址下文“配置链路”一节会看到。total本测试中该类别泄漏对象的总数。objects下的类型名如FocusNode泄漏对象的具体 Dart 类型直接告诉你该去检查哪一类资源的创建点。test泄漏发生的具体测试名便于在多测试文件中定位。identityHashCode对象身份哈希用于在同一份报告中区分同类型的多个实例。泄漏跟踪在框架中的配置链路官方文档指出泄漏跟踪的开关与全局行为统一配置在 packages/flutter/test/flutter_test_config.dart 的testExecutable中。这是flutter_test对packages/flutter/test目录下每一个测试库都会执行的入口钩子开启后的完整配置代码如下if (_isLeakTrackingEnabled()) { LeakTesting.enable(); LeakTracking.warnForUnsupportedPlatforms false; // Customized link to documentation on how to troubleshoot leaks, // to print in the error message. LeakTracking.troubleshootingDocumentationLink https://github.com/flutter/flutter/blob/main/docs/contributing/testing/Leak-tracking.md; LeakTesting.settings LeakTesting.settings.withIgnored(createdByTestHelpers: true); }逐项解读这四行配置见 flutter_test_config.dartLeakTesting.enable()全局启用testWidgets的泄漏检测。未调用该函数时testWidgets不做任何泄漏跟踪。LeakTracking.warnForUnsupportedPlatforms false关闭“当前平台不完全支持泄漏跟踪”的警告输出保持测试日志干净。LeakTracking.troubleshootingDocumentationLink ...把失败报告中打印的排障链接定制为 Flutter 仓库内的这篇文档使贡献者在 CI 和本地看到的都是同一份权威指引。LeakTesting.settings.withIgnored(createdByTestHelpers: true)把“由测试助手test helpers创建的对象”默认豁免。这是一个很实用的细节——框架自身大量测试工具类会创建并持有对象若不豁免这些对象会被误报为泄漏噪声会淹没真实问题。同一个testExecutable里还顺带启用了两项与泄漏排查强相关的严格检查debugCheckIntrinsicSizes true; WidgetController.hitTestWarningShouldBeFatal true;前者让大量RenderBox实现享受额外的 intrinsic 尺寸校验后者让tap()等手势操作在命中测试失败时直接报错二者配合让泄漏/状态不一致类问题更容易在测试阶段暴露。依赖层面泄漏跟踪的三方依赖在 packages/flutter/pubspec.yaml 中以 dev 依赖形式声明leak_tracker、leak_tracker_testing、leak_tracker_flutter_testing均为any由 monorepo 的引擎/工具链统一锁定版本而 packages/flutter_test/pubspec.yaml 则声明了leak_tracker_flutter_testing: ^3.0.10作为普通依赖——这说明泄漏跟踪能力是flutter_test的内置组成部分任何使用testWidgets的测试天然具备该 API 的可用性。针对单个测试的豁免experimentalLeakTesting 参数除了全局开关testWidgets本身提供了一个实验性参数用于精细控制。见 packages/flutter_test/lib/src/widget_tester.dart 的 API 文档/// The argument [experimentalLeakTesting] is experimental and is not recommended /// ... /// When [experimentalLeakTesting] is set, it is used to leak track objects created /// in the body of the test... /// Adjust [LeakTesting.settings] in flutter_test_config.dart /// /// To turn off leak tracking just for one test, set [experimentalLeakTesting] to /// LeakTrackingForTests.ignore().其内部实现位于 widget_tester.dart当参数传入时testWidgets会在测试体执行前后分别调用maybeSetupLeakTrackingForTest/maybeTearDownLeakTrackingForTest把泄漏跟踪的生命周期严格限定在该测试范围内未传参时则回退到全局的LeakTesting.settings。由此可归纳出三个层级的控制粒度层级手段适用场景全局整个 test 目录--dart-define LEAK_TRACKINGtrue或export LEAK_TRACKINGtrue本地验证、CI 分片单测试testWidgets(..., experimentalLeakTesting: LeakTrackingForTests.ignore())该测试本身因异常路径等合理原因无法保证 dispose全局豁免规则在flutter_test_config.dart中调整LeakTesting.settings如withIgnored(createdByTestHelpers: true)对整类对象/创建来源做系统性豁免何时可以豁免泄漏跟踪官方文档对豁免opt-out的态度非常明确默认应当启用泄漏跟踪以验证所有 disposable 都被正确 dispose如果某个测试确实被豁免必须在代码注释中清晰说明原因。文档给出了目前唯一被认可的典型豁免场景当测试抛出异常、导致代码没有走正常的收尾finalize流程时可以豁免该测试。同时文档也明确提醒一些异常路径本应保证对象被正常释放理想情况下不应产生泄漏“确保异常代码路径中 disposable 被正确释放”这一工程目标目前尚未被优先处理上游跟踪问题为 issue #157470。因此豁免是面向“测试预期会抛异常”的兜底手段而不是逃避修复的理由——如果测试本身不应抛异常正确做法是修复代码使对象生命周期闭合而不是加ignore()。默认状态CI 中的 leak_tracking 分片文档说明泄漏跟踪在 Flutter 测试基础设施中是默认开启于专用分片的。以当前仓库的 .ci.yaml 为准目前共有 3 个 Windows 泄漏跟踪分片文档撰写时为 2 个当前仓库已扩展为 3 个Windows framework_tests_libraries_leak_trackingsubshard: librariesWindows framework_tests_misc_leak_trackingsubshard: miscWindows framework_tests_widgets_leak_trackingsubshard: widgets以 libraries 分片为例其完整配置片段如下- name: Windows framework_tests_libraries_leak_tracking recipe: flutter/flutter_drone timeout: 120 properties: test_timeout_secs: 3600 # 1 hour dependencies: - [ {dependency: goldctl, version: git_revision:c845c41b9b81bfcb11f2f0ab17b5b2386d634c31} ] shard: framework_tests subshard: libraries tags: [framework, hostonly, shard, windows] leak_tracking: true test_randomization_off: true几个配置点值得注意leak_tracking: true分片级属性由测试执行器读取后等价于向flutter test注入--dart-define LEAK_TRACKINGtrue与本地手动开启的方式完全一致。该属性的合法性由 dev/bots/test/ci_yaml_validation_test.dart 中的校验约束expect(properties[leak_tracking], anyOf(true, false, isNull))即只能是true、false或缺省。test_randomization_off: true泄漏分片关闭测试随机化。从源码结构看这是为了在出现泄漏时获得可复现、可稳定归因的执行顺序。timeout: 120与test_timeout_secs: 3600分片整体 2 小时超时单个测试 1 小时——泄漏跟踪会带来额外开销因此这些分片是独立的、与常规框架测试分片解耦的运行单元。分片的runIf触发条件覆盖dev/**、packages/flutter/**等路径保证框架代码变更时泄漏分片会随之运行。这些分片的机器人运行状态可在 Flutter build dashboard 上查看flutter-dashboard.appspot.com用于跟踪泄漏跟踪在主干上的长期健康度。排障实践建议综合以上机制一条典型的泄漏修复路径是复现本地运行flutter test --dart-define LEAK_TRACKINGtrue 目标测试文件定位从失败报告中读取泄漏对象类型如FocusNode与具体测试名比对在测试体内搜索该类型对象的创建点确认对应的dispose()是否在tearDown或测试结束前调用注意框架已默认豁免 test helpers 创建的对象若报告指向此类对象应优先检查豁免设置决策若是真实的 dispose 遗漏修复代码若测试预期抛异常且无法在异常路径收尾按规范加注释说明后使用experimentalLeakTesting: LeakTrackingForTests.ignore()豁免。如果你还需要在测试中对内存行为做更主动的断言而不只是被动检测泄漏可进一步参考同目录下的 How-to-write-a-memory-test-for-Flutter.md两篇文档互为补充前者教你编写内存相关的测试本文覆盖的是测试基础设施层面的泄漏自动检测。小结开启方式只有两种编译期--dart-define LEAK_TRACKINGtrue或运行期export LEAK_TRACKINGtrue二者在 flutter_test_config.dart 中按“或”关系生效Web 平台恒为关闭全局配置集中于testExecutable钩子启用跟踪、抑制平台警告、定制排障文档链接、豁免 test helpers 创建的对象testWidgets的experimentalLeakTesting参数提供单测试级豁免文档明确豁免必须附注释说明且当前唯一被认可的场景是“测试抛异常导致收尾流程未执行”CI 侧以独立的 Windows 分片libraries / misc / widgets默认运行泄漏跟踪并配合关闭测试随机化与更长的超时预算其属性取值由 CI 校验测试强制约束。【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表