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

资讯详情

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

Pigeon 平台测试中的共享 Dart 代码包:shared_test_plugin_code 如何统一两个原生插件的测试骨架

Pigeon 平台测试中的共享 Dart 代码包:shared_test_plugin_code 如何统一两个原生插件的测试骨架 Pigeon 平台测试中的共享 Dart 代码包shared_test_plugin_code 如何统一两个原生插件的测试骨架【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packagesshared_test_plugin_code是 Pigeon 仓库platform_tests目录下负责“跨插件共享 Dart 代码”的内部包它集中存放 Pigeon 生成的 Dart 输出、示例 App 与集成测试供test_plugin与alternate_language_test_plugin两个原生测试插件复用。读完本文你可以理解 Flutter 插件体系在“一个平台只能注册一种语言实现”的约束下如何通过共享包消除两侧 Dart 测试逻辑的重复并能准确找到生成物、集成测试入口、mock 再生成脚本以及测试驱动脚本在仓库中的位置。为什么需要一个共享代码包Pigeon 的平台级测试platform_tests由两个插件工程组成test_plugin各平台“默认首选插件语言”的统一测试骨架覆盖 KotlinAndroid、SwiftiOS/macOS、CLinux与 CWindowsalternate_language_test_plugin面向多语言平台的“备选语言”目前覆盖 Android 的 Java 与 iOS 的 Objective-C。shared_test_plugin_code 的 README 给出了这个共享包存在的根本原因这两个插件项目在设计上是完全一致的intended to be identical之所以必须拆成两个独立项目仅仅是因为 Java/Kotlin 与 Obj-C/Swift 的语言重叠使得它们无法合并到同一个插件里因此几乎所有的 Dart 代码都应该放在这个包中而不是放在插件内部。从 Flutter 插件机制看这一约束是成立的插件注册pubspec.yaml的flutter.plugin.platforms每个平台只能指定一个pluginClass。test_plugin的 pubspec.yaml 中Android 注册com.example.test_plugin.TestPluginKotlin 实现、Windows 注册TestPluginCApi而 alternate_language_test_plugin 的 pubspec.yaml 中 Android 注册com.example.alternate_language_test_plugin.AlternateLanguageTestPluginJava 实现。同一个平台不能同时挂 Kotlin 与 Java 两套实现所以测试骨架被迫拆成两个插件工程。共享包的设计正是为了在这种拆分下保证 Dart 侧只有一份事实来源single source of truth。说明README 中写作alternate_language_shared_plugin而仓库中对应的实际目录名为 alternate_language_test_plugin。共享包里到底放了什么对照 shared_test_plugin_code 目录结构可以把它的内容归纳为四类恰好与 README 所说的“generated Pigeon output, example app, integration tests”一一对应。1. Pigeon 生成的 Dart 输出lib/src/generated/lib/src/generated/ 下有 15 个.gen.dart文件覆盖了 Pigeon 各类生成场景的 Dart 端生成文件覆盖的 Pigeon 特性core_tests.gen.dart核心 API/数据类互调与两侧原生.gen文件对应enum.gen.dart枚举映射event_channel_tests.gen.dartEventChannel 风格的流式回调event_channel_without_classes_tests.gen.dart不依赖类的 EventChannelmessage.gen.dart基础消息结构multiple_arity.gen.dart参数数量较多的方法aritynative_interop_tests.gen.dart 及 .ffi / .jni 变体Native InteropFFI/JNI 路径non_null_fields.gen.dart / null_fields.gen.dart可空/非空字段语义nullable_returns.gen.dart可空返回值primitive.gen.dart原始类型proxy_api_tests.gen.dartProxy API仅 Kotlin/Swift 支持见下文flutter_unittests.gen.dartFlutter 单测场景这些生成物对外通过一个 barrel 文件 lib/generated.dart 统一导出如HostIntegrationCoreApi、ProxyApiTestClass等各插件的示例 App 与集成测试只需import generated.dart即可。2. 集成测试逻辑lib/*.dartlib/integration_tests.dart约 2600 行是共享包的主体它按“目标生成器”参数化地组织所有集成测试。其中两个关键定义/// Possible host languages that test can target. enum TargetGenerator { cpp, // Windows C gobject, // Linux GObject java, kotlin, // Android objc, swift, // iOS/macOS } /// Host languages that support generating Proxy APIs. const SetTargetGenerator proxyApiSupportedLanguages TargetGenerator{ TargetGenerator.kotlin, TargetGenerator.swift, };见 integration_tests.dart#L22-L46。入口函数runPigeonIntegrationTests(TargetGenerator targetGenerator)由两个插件的 example 集成测试调用L49从而用同一套 Dart 断言去驱动 Java/Kotlin 或 Obj-C/Swift 的原生实现。同目录下的 native_interop_integration_tests.dart、proxy_api_integration_tests.dart、test_types.dart 与 comparison_benchmarks.dart 分别承载 FFI 互操作测试、Proxy API 测试、共享测试类型与对比基准。3. 示例 Appexample applib/example_app.dart 提供一个极简ExampleApp在initPlatformState中创建HostIntegrationCoreApi并调用api.noop()用于“验证 Pigeon 能够成功调用原生代码”界面上显示Calling.../Success!/Failed: ...。其源码注释明确说明真正的测试全部在集成测试中完成它们运行在 example app 的上下文里但并不依赖这个 UI 类。4. Dart 单元测试test/test/ 下是一批纯 Dart 单元测试如 primitive_test.dart、multiple_arity_test.dart、null_fields_test.dart、proxy_api_overrides_test.dart 等配合 mockito 生成的.mocks.dart文件对生成代码的 Dart 端行为做回归验证。包配置几处值得注意的细节pubspec.yaml为什么 flutter_test / integration_test 是普通依赖pubspec.yaml 的关键信息name: shared_test_plugin_code description: Common code for test_plugin and alternate_language_test_plugin version: 0.0.1 publish_to: none # 纯内部包不发布到 pub.dev environment: sdk: ^3.10.0 flutter: 3.38.0 dependencies: build_runner: ^2.1.10 ffi: ^2.2.0 flutter: {sdk: flutter} # These are normal dependencies rather than dev_dependencies because the # package exports the integration test code to be shared by the integration # tests for the two plugins. flutter_test: {sdk: flutter} integration_test: {sdk: flutter} jni: ^1.0.3 meta: ^1.17.0 mockito: ^5.4.4 objective_c: ^9.5.0 dev_dependencies: ffigen: ^22.0.0 jnigen: ^1.0.0 leak_tracker: any其中两条注释解释了设计取舍flutter_test与integration_test之所以放在dependencies而非dev_dependencies是因为本包要把集成测试代码导出给两个插件的集成测试使用——dev_dependencies不会传递给依赖方ffi、jni、objective_c是 Dart 侧直接调原生Native Interop 路径所需的运行期依赖ffigen、jnigen则作为 dev 依赖用于在本地重新生成 FFI/JNI 绑定相关配置位于 test_plugin/tool/pigeon/ 下的native_interop_tests_ffigen_config.dart、native_interop_tests_jnigen_config.dart。另外注意版本基线差异共享包要求 Dart^3.10.0/ Flutter3.38.0而 test_plugin 要求 Dart^3.12.0/ Flutter3.44.0说明统一测试骨架对 Flutter 版本的要求更高。dart_test.yaml 与 mock 再生成dart_test.yaml 只有一行test_on: vm即包内单元测试只在 Dart VM 上运行不涉及浏览器/WASMregenerate_mocks.sh 的内容是dart run build_runner build --delete-conflicting-outputs用于在修改 mock 注解后重新生成test/*.mocks.dart。两个消费方如何接入test_plugin首选语言插件注册pubspec.yaml#L10-L25Android →com.example.test_plugin.TestPluginiOS/macOS →TestPlugin且sharedDarwinSource: trueLinux →TestPluginWindows →TestPluginCApi各平台生成物Android 的 CoreTests.gen.kt 等 Kotlin 文件、macOS/iOS 的 CoreTests.gen.swift、Linux 的 core_tests.gen.cc、Windows 的 core_tests.gen.cpp原生单元测试Android 侧 android/src/test/kotlin/ 下的AsyncHandlersTest.kt、MultipleArityTests.kt等iOS/macOS 侧 example/ios/RunnerTests/ 下的 Swift 测试集成测试入口example/integration_test/test.dart以对应平台的TargetGenerator调用共享包的runPigeonIntegrationTestsDart 侧的示例 App 即共享包的 example_app.dart。alternate_language_test_plugin备选语言平台范围更小仅 Android 与 iOS/macOSpubspec.yaml#L10-L21没有 Linux/Windows 配置Android 实现为 JavaCoreTests.java原生单测在 android/src/test/java/ 下的PrimitiveTest.java、AsyncTest.java等iOS 实现为 Objective-CCoreTests.gen.m 与 AlternateLanguageTestPlugin.m原生单测为 example/ios/RunnerTests/ 下的.m测试其集成测试入口同样是 example/integration_test/test.dart。由于两侧 example 的 Dart 入口、生成物与测试断言全部来自共享包新增或修改 Pigeon 的 Dart 端测试逻辑时只需改一处两侧插件自动同步——这正是 README 所说“almost all Dart code should be in this package”的落地方式。如何运行这些测试platform_tests 的 README 说明了完整的驱动方式推荐方式使用test.dart一键运行。该脚本会先从 pigeons/ 目录中的 Pigeon 定义重新生成原生代码写入各平台测试骨架如上文列出的CoreTests.gen.kt/CoreTests.gen.m/core_tests.gen.cc等然后驱动对应平台的原生测试与集成测试IDE 内直接运行如果是在平台 IDE 中直接执行可先用generate.dart生成必要的 Pigeon 输出再手动运行原生/集成测试。此外README 还保留了flutter_null_safe_unit_tests一节这是 NNBD 成为 Pigeon 唯一支持模式之前的遗留 Dart 单测结构其计划是“folded back into the main tests”合并回主测试阅读仓库时遇到该历史命名不必当作新机制。小结shared_test_plugin_code解决的是结构性重复问题两个原生测试插件因 Java/Kotlin、Obj-C/Swift 无法合并而被迫拆分但 Dart 侧的生成物、示例 App 与集成测试必须保持一致因此集中收口到这一个publish_to: none的内部包它的内容由四块构成lib/src/generated/下 15 个 Pigeon Dart 生成物、integration_tests.dart等按TargetGenerator参数化的集成测试、极简验证用的ExampleApp、以及test/下的 Dart 单元测试与 mockito 产物配置上有两个易被忽视的点flutter_test/integration_test必须作为普通依赖才能传递给消费方dart_test.yaml的test_on: vm限定了单元测试运行环境运行入口统一收敛到 tool/test.dart重新生成 驱动或 tool/generate.dart仅生成维护约定今后新增 Dart 端测试代码时优先放入共享包而非两个插件各自的lib/以保持“两侧插件 intended to be identical”的设计前提不被破坏。【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表