首页/新闻资讯/正文详情

工具调用已足够,现在基于 pkg/testing/README.md 及其源码完成文章编写。

发布时间:2026/9/25 5:35:50 来源:云帆数科 栏目:资讯中心
工具调用已足够,现在基于 pkg/testing/README.md 及其源码完成文章编写。
编程语言编译器语言运行时标准库开发工具【免费下载链接】sdkThe Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.项目地址https://gitcode.com/gh_mirrors/sdk1/sdk点击查看免费下载output文章Dart SDK 测试基础设施实战深入解析 pkg/testing 的 Chain/Dart 双套件测试框架pkg/testing是 Dart SDK 中一套不带电池without batteries的测试基础设施它不提供断言库而是专注于解决一个核心难题——如何用一套统一的配置去测试由多个独立步骤组成如 dart2js 编译 JS 引擎执行的工具链。本文以 pkg/testing/README.md 为主线结合 pkg/testing/lib/ 下的真实实现源码完整讲解testing.json配置、Chain与Dart两类测试套件的编写方式、与package:test/test.dart的集成方法以及底层测试发现、代码生成与分析机制。读完本文你将能够为自己的编译工具链或 Dart 包搭建一套支持分步执行、状态期望status、自动静态分析的测试运行环境。这个包是什么定位与动机pkg/testing的定位可以概括为三句话引自 README分步测试工具链可以把一个编译器等工具链拆成多个独立步骤来测试运行独立测试测试是带有main方法的独立 Dart 程序可以直接运行保证无警告用dartanalyzer确保所有测试与实现代码没有静态分析问题。同时它明确不是package:test的替代品——恰恰相反package:test被视为这个包里没带的电池the batteries that arent included。pkg/testing可以用来驱动运行package:test编写的测试两者是协作而非竞争关系。这套设计的动机源于 Dart 团队测试真实工具链的经验工具链测试步骤dart2js① 对 Dart 源文件执行 dart2js产出 JavaScript 文件② 在 JS 解释器如 d8上运行该 JS 文件并报告是否抛出异常Dart VM① 在 Dart VM 中运行 Dart 源文件并报告是否抛出异常单步dartanalyzer① 分析 Dart 源文件并报告是否存在问题单步pub transformer 等可以有更多步骤更为关键的是多个工具链常常共享同一份输入源文件并且应该对行为达成一致。例如hello-world.dart用 dart2js 编译后在 d8 上运行不应抛异常、在 Dart VM 上运行也不应抛异常、用 dartanalyzer 分析应无报告。这正是 Dart SDK 中language、corelib等测试目录被多个编译器/运行时同时消费的原因。此外还有一个性能上的动机Dart 实现的编译器单元测试在同一 Dart VM 进程中运行时通常更快动态优化会生效因此希望有一个把所有测试合并进同一个 Dart 程序的能力而开发期又希望只运行单个测试。pkg/testing的两种套件类型正是为这两类需求设计的。两类测试套件Chain 与 Dart基于上述动机pkg/testing定义了两种套件suite套件就是一组测试的集合Chain面向工具链的测试套件详见下文Chain 套件Dart面向纯 Dart 单元测试的套件详见下文Dart 套件。对应到源码两者都是 suite.dart 中Suite抽象基类的子类Chain定义在 chain.dartAnalyze即配置文件中analyze段定义在 analyze.dart。注意Suite.fromJsonMap目前只识别chain这一种 kind未知 kind 会直接抛异常suite.dart。快速开始配置 testing.json 并运行按 README接入只需两步创建一个名为testing.json的配置文件运行bin/run_tests.dart。一个最小可用的配置是这样的{ }它甚至不包含任何套件——test_root.dart 中的addDefaults会为缺失的键自动填充默认值packages默认为.packagessuites默认为空数组analyze段默认提供空的uris与exclude。这也是pkg/testing自带配置的样子见 pkg/testing/testing.json它额外指定了packages: ../../.dart_tool/package_config.json和一组待分析的 URI。关于运行入口的说明README 写的是bin/run_tests.dart而在当前仓库中该入口已位于 pkg/testing/lib/src/run_tests.dart其main函数负责解析命令行、读取配置并驱动整个流程。此外run与runMe两个高层入口由 testing.dart 统一导出export src/run.dart show run, runMe;。run_tests.dart的命令行支持解析逻辑见 run_tests.dart--verbose或-v开启详细输出否则会提示Use --verbose to display more details.--skipsuite1,suite2跳过指定套件-Dkeyvalue向测试注入环境变量解析为MapString, String最终会序列化进生成的测试程序--configfile显式指定配置文件若缺省则在当前目录找testing.json找不到时会在test/目录树内递归搜索唯一的testing.json位置参数为选择器selectors用于过滤要运行的测试。配置详解testing.json 的三段式结构配置文件的顶层结构源码解析见 test_root.dart为键含义默认值packages相对配置文件的包配置.packages/package_config.jsonURI.packagessuites测试套件列表Chain套件[]analyze静态分析配置段uris、exclude可选options、git grep空的uris/exclude所有相对 URI 都以配置文件自身的位置为基准进行解析base.resolve(...)目前只支持 file URI。Chain 套件分步测试任意工具链配置属性Chain套件可以测试任何能被拆分为一个或多个步骤的事物。README 给出的完整示例README{ suites: [ { name: golden, kind: Chain, source: test/golden_suite.dart, path: test/golden/, status: test/golden.status, pattern: [ \\.dart$ ], exclude: [ ] } ] }各属性含义结合 chain.dart 的Chain.fromJsonMap可看到更多细节属性含义源码补充说明name套件名称如 SDK 中的language、corelib生成的测试程序中套件源文件以该名字作为 import 别名writeImportOnkind固定为ChainSuite.fromJsonMap按小写匹配source相对 URI指向实现套件步骤的 Dart 程序解析后用于 import 与生成闭包调用path相对配置文件的根目录是该套件所有测试文件的根解析时若不以/结尾会自动补上等价于源码中的rootstatus相对配置文件的状态文件 URI用readTestExpectations解析pattern匹配测试文件名的正则列表每个元素RegExp(p)用path.contains(r)匹配exclude排除文件的正则列表命中即跳过源码中Chain还额外支持subRoots、includeEndsWith、excludeContains、excludeEndsWith等进阶过滤项chain.dart。当subRoots未提供时默认以root为唯一子根进行递归文件列举。实现一个 Chain 套件source指向的 Dart 程序必须导出一个固定签名的顶层函数READMEFutureChainContext createContext(Chain suite) async { ... }在真实源码中该签名被定义为typedef CreateContextchain.dart并且第二个参数是MapString, String environment命令行-D注入的环境变量typedef CreateContext FutureChainContext Function( Chain suite, MapString, String environment, );套件需要实现ChainContext的子类用steps列表定义组成链的各步骤步骤是Step的子类。第一步的输入是TestDescription第 n1 步的输入是第 n 步的输出数据在步骤之间以流水线方式流动。README 给出的在 Dart VM 上跑测试的完整套件import testing.dart; FutureChainContext createContext( Chain suite, MapString, String environment) async { return new VmContext(); } class VmContext extends ChainContext { final ListStep steps const Step[const DartVmStep()]; } class DartVmStep extends StepTestDescription, int, VmContext { const DartVmStep(); String get name Dart VM; FutureResultint run(TestDescription input, VmContext context) async { StdioProcess process await StdioProcess.run(dart, [input.file.path]); return process.toResult(); } } main(ListString arguments) runMe(arguments, createContext);这里StepTestDescription, int, VmContext的三个类型参数分别对应输入类型、输出类型、上下文类型。Step基类chain.dart提供三个便捷的Result构造函数pass(output)步骤通过并把输出交给下一步fail(output, [error, trace])步骤失败crash(error, trace)步骤崩溃未捕获异常时默认调用unhandledError。ResultOchain.dart携带输出值、期望状态Expectation、错误信息与日志列表还支持autoFixCommand与canBeFixWithUpdateExpectations两个用于自动修复期望文件的钩子。多步骤链的示例可以参考 kernel 包的test/closures/suite.dart该套件把共享的AnalysisContext存放在TestContext中从而让所有测试复用同一个分析上下文。步骤的同步与异步isAsync 语义这是 Chain 框架里最容易踩坑、也最能体现设计深度的部分。Step.isAsyncchain.dart默认为false当isAsync false每步返回的 future 会被 await同一时刻只有一个测试在运行步骤严格顺序执行当isAsync true该步不阻塞测试调度而是把自身 future 挂入一个全局列表最终统一Future.wait下一个测试可以在该步完成前就开始与外部进程如 VM、JS 解释器并行执行。框架的doStep逻辑chain.dart会在每步完成后检查result.outcome若为pass则把result.output作为输入继续下一步否则停止并进入清理cleanUp与结果比对。最终结果与status文件中的期望比对不匹配则记入unexpectedResults并把进程退出码置为 1。从配置到执行runChain 的调用链当套件被选中运行时SuiteRunner.generateDartProgramrun.dart会为每个 Chain 套件生成两段代码writeImportOnimport source as name;writeClosureOnawait runChain(name.createContext, {...environment}, selectors, r套件JSON);最终生成一个完整的 Dart 程序library testing.generated;。runChainchain.dart再从 JSON 文本重建Chain对象、调用createContext获得上下文并执行context.run(...)。也就是说套件的 JSON 配置最终以源码形式编译进生成的测试程序里。Dart 套件把单元测试合并进同一个进程Dart套件用于运行用 Dart 编写的单元测试。每个测试都是带main方法的独立 Dart 程序可以直接从命令行运行README。配置属性完整示例{ suites: [ { name: my-package, path: test/, kind: Dart, pattern: [ _test\\.dart$ ], exclude: [ /test/golden/ ] } ] }属性含义name套件名称简单包可用test或包名SDK 中常用被测组件名如dart2jspath相对配置文件的测试根目录只支持 file URIkind固定为Dartpattern匹配测试文件名的正则列表exclude排除文件的正则列表注意Dart套件在Suite.fromJsonMap中并不单独创建对象而是由测试发现逻辑负责。每个Dart测试文件由FileBasedTestDescriptiontest_description.dart表示它把相对根的路径去掉.dart后缀作为shortName并用writeImportOn/writeClosureOn生成import uri as name show main;和shortName: name.main,两段代码——所有测试以库library的身份被 import 进同一个程序而不是文本拼接因此每个测试拥有独立的词法作用域。Well-Behaved Tests套件对测试的约定把大量测试合并进同一进程意味着必须有一致的行为约定README所有测试使用同一个包配置文件.packages/package_config.json。测试不是被拼接而是被独立 import 的所以它们必须共享包解析环境异步测试从main返回一个Future。测试按顺序运行框架会等待当前测试main返回的 future 完成无论成功或报错后才启动下一个异步测试应当在该 future 完成时结束所有异步操作测试必须管理全局状态静态状态。简单说就是自己打扫卫生或者约定在每个测试运行前重置共享状态、运行后不清理只要所有测试对共享全局状态的管理方式达成一致即可。运行顺序与等一个再启下一个的语义在生成程序的runTestsrun_tests.dart中有直接体现它按tests.keys顺序遍历逐个await执行。而最终生成的程序通过Isolate.spawnUri以暂停启动paused: true的隔离区isolate运行错误通过errorsAreFatal: false捕获后向上传播run.dart。Dart套件里你可以自由使用任何测试框架如package:test前提是它对全局状态足够守规矩。配置静态分析analyze 段默认情况下Dart套件中的所有测试都会被dartanalyzer分析。README 给出带分析配置的完整示例README{ suites: [ { name: my-package, path: test/, kind: Dart, pattern: [ _test\\.dart$ ], exclude: [ /test/golden/ ] } ], analyze: { uris: [ lib/, ], exclude: [ /third_party/ ] } }analyze段的属性源码见 analyze.dart属性含义uris相对配置文件的、额外需要分析的 URI 列表目前仅支持 file URIexclude正则列表匹配到的文件仍会被分析但诊断被抑制并忽略options额外的分析选项文件如analysis_options.yaml通过--options传给分析器git grep可选{ pathspecs: [...], patterns: [...] }用git grep -l把命中文件加入待分析集合默认 pathspecs 为.真正的分析动作在analyzeUrisanalyze.dart中完成值得注意的实现细节目录 URI 会被递归展开只收集.dart文件分析器固定指向pkg/analyzer_cli/bin/analyzer.dart相对仓库根的 pkg/analyzer_cli/bin/analyzer.dart以--batch模式启动stdin 写入全部参数并以--formatmachine输出机器可读诊断诊断输出逐行解析为AnalyzerDiagnostic包含 kind、code、URI、行列号、消息只报告那些仍在待分析集合内的文件任何非空输出都会导致Non-empty output from analyzer.异常——这正是所有测试与实现无警告的强制手段SDK 路径通过_findSdkPath依据可执行文件旁的version/dart-sdk/version文件探测。pkg/testing自带的 testing.json 就是这种写法的实例analyze.uris覆盖了bin/、lib/、test/并指定options: analysis_options.yaml。与其它测试运行器的集成通过test.dart运行要在一个_test.dart驱动的入口里运行套件my_suite创建如下文件READMEimport package:expect/async_helper.dart show asyncTest; import package:testing/testing.dart show run; main(ListString arguments) asyncTest(run(arguments, [my_suite]));通过package:test运行同样运行my_suite但挂在package:test下READMEimport package:test/test.dart show Timeout, test; import package:testing/testing.dart show run; main() { test(my_suite, () run([], [my_suite]), timeout: new Timeout(new Duration(minutes: 5))); }这里导出的run函数run.dart会定位testing.json→ 按名字过滤套件 →generateDartProgram生成合并程序 →analyze执行静态分析 →runProgram在隔离区中运行。之所以建议给package:test用例设置 5 分钟超时是因为整条 Chain 工具链可能包含多步外部进程耗时会远超默认超时。状态文件与期望机制Chain套件的status属性指向一个状态文件格式为每行一条测试名: 期望状态列表解析实现见 status_file_parser.dart# 注释以 # 开头行内 # 之后的内容也会被剔除 my_test: Pass crashy_test: Crash, Timeout slow_test: Skip要点未在状态文件中出现的测试默认期望是Passstatus_file_parser.dart期望状态名由ExpectationSet定义expectation.dart内置pass、crash、timeout、fail、skip以及missingcompiletimeerror、missingruntimeerror、runtimeerror等特殊期望也支持通过 JSON 列表自定义期望框架会比对实际结果与期望集合只有不在期望集合内或不属于其 canonical 组的结果才被判定为意外失败并令退出码为 1chain.dartSkip期望组会直接跳过该测试不执行。这一步实现了多工具链共享同一份输入、各自声明期望的诉求同一份测试文件dart2js 与 Dart VM 可以有不同的 status 文件各自独立维护期望。总结pkg/testing 的适用场景综合 README 与源码pkg/testing最擅长的是工具链分步测试通过Chain套件 Step流水线把编译 → 运行 → 校验这类多阶段验证建模为可复用的步骤链每步产出喂给下一步且支持isAsync并行调度外部进程大量单元测试的合并执行通过Dart套件把众多独立main测试 import 进同一个 Dart 程序在同一进程内顺序执行享受 JIT 动态优化带来的加速同时支持想跑单个就传 selector、想跑全部就跑一次质量闸门analyze段强制所有测试与实现代码通过静态分析零警告与生态共存可嵌入test.dart或package:test作为宿主运行器是自带电池的测试框架之外的一块公共地基。若你需要为某个编译工具链或大型 Dart 包搭建类似的测试底座完全可以参照 pkg/testing/testing.json 起步并阅读 pkg/testing/lib/src/chain.dart、pkg/testing/lib/src/run.dart 与 pkg/testing/lib/src/analyze.dart 来深入理解其运行机制。赞分享编程语言编译器语言运行时标准库开发工具【免费下载链接】sdkThe Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.项目地址https://gitcode.com/gh_mirrors/sdk1/sdk点击查看免费下载相关推荐已收集足够资料现基于关联文档及仓库源码完成文章撰写已收集足够资料现基于关联文档及仓库源码完成文章撰写 PostGraphile V5 实时Realtime能力实战用订阅Subscriptions构建后端API网关已收集足够信息现在开始撰写文章。已收集足够信息现在开始撰写文章。 基于对关联文档 080_wp8_aggregators.md https://link.gitcode.com/i/71ad已完成源码调研信息充分现在撰写文章已完成源码调研信息充分现在撰写文章 我已完成对该文档及 curl 仓库中 Expect: 100 continue 相关实现lib/http.c 状态机、CLI网络通信上一篇OpenAPI实时监控终极指南7×24小时守护API健康下一篇Standard Notes CI/CD流水线自动化构建与部署终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

WeiXinMPSDK 高级接口实战指南:AppId 与 AccessToken 的自动识别调用机制
WeiXinMPSDK 高级接口实战指南:AppId 与 AccessToken 的自动识别调用机制

后端即时通讯金融科技 【免费下载链接】WeiXinMPSDK 微信全平台 .NET SDK, Senparc.Weixin for C#,支持 .NET Framework 及 .NET Core、.NET 10.0。已支持微信公众号、小程序、小游戏、微信支付、企业微信/企业号、开放平台、JSSDK、微信周边等全平台。 … · 2026/9/25 5:35:44

【Dify】智能简历筛选与语义分析应用
【Dify】智能简历筛选与语义分析应用

自动化与智能化简历筛选已成为招聘流程提升效率的重要方向。简历文件数量庞大、信息结构多样,传统人工处理耗时费力,容易出现筛选误差。 本文介绍一种基于多模型与自动化节点的智能简历筛选工作流,覆盖批量导入、文本抽取、结构化分析到语义筛查与结果导出,助力高效实现精… · 2026/9/25 5:35:44

rsuite Avatar 头像组件 bordered 边框属性实战指南:从示例到源码实现
rsuite Avatar 头像组件 bordered 边框属性实战指南:从示例到源码实现

前端UI组件 【免费下载链接】rsuite 🧱 A suite of React components . 项目地址: https://gitcode.com/gh_mirrors/rs/rsuite 点击查看 免费下载 导读 bordered 是 rsuite Avatar(头像)组件自 5.59.0 版本起提供的属性&#x… · 2026/9/25 5:35:44

PaddleSpeech SentencePiece 子词建模实战:从零训练 unigram/BPE 分词器并接入 ASR 全流程
PaddleSpeech SentencePiece 子词建模实战:从零训练 unigram/BPE 分词器并接入 ASR 全流程

人工智能语音音频NLP媒体生成 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation … · 2026/9/25 5:59:08

SchoolDB四张空表的设计与验证:从零搭建学校管理系统数据库
SchoolDB四张空表的设计与验证:从零搭建学校管理系统数据库

SchoolDB,4张表,无数据。看到这个标题的时候,我第一反应是:这不就是我当年做数据库课程设计时的日常吗?建好了库,建好了表,然后对着空荡荡的几张表结构发呆,不知道下一步该干什么。很… · 2026/9/25 5:59:08

Mac磁盘空间清理全攻略:从180G降到40G的实战经验
Mac磁盘空间清理全攻略:从180G降到40G的实战经验

1. 从“系统数据”吃掉上百G说起:Mac磁盘到底被什么塞满了很多人第一次看到“关于本机 → 储存空间”里那条“系统数据”时,都会愣一下:明明没存多少照片、没装几个大软件,怎么系统数据能占上百G?更离谱的是&#xff0… · 2026/9/25 5:59:08

DotNETReactor代码保护原理与合规实践指南
DotNETReactor代码保护原理与合规实践指南

简介:本资源为DotNET Reactor最新破解版工具集,面向C#开发者与.NET程序安全加固需求者,解决C#代码易被反编译、逻辑泄露等核心防护难题,适用于软件发布前的代码混淆、加密与许可证控制等实战场景。压缩包共105个文件,含… · 2026/9/25 5:59:02

Motrix浏览器扩展连接失败的底层原因与解决方案
Motrix浏览器扩展连接失败的底层原因与解决方案

1. Motrix浏览器扩展到底在解决什么问题?——不是插件本身,而是RPC通信链路的“最后一公里”Motrix作为一款开源的下载管理器,它的浏览器扩展(Browser Extension)本质是个轻量级“遥控器”,不直接处理下载任… · 2026/9/25 5:59:02

系统Python拒绝pip安装?PEP 668报错解决与虚拟环境实践
系统Python拒绝pip安装?PEP 668报错解决与虚拟环境实践

最近几天我在好几个技术群里看到同一张截图,红屏黑字写着error: externally-managed-environment,后面跟着一大段"This environment is externally managed"的解释。问这个问题的,有刚给树莓派换完系统的硬件玩家,有在 … · 2026/9/25 5:59:02

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码