Deno 如何在 Deno 中运行 node:* 兼容层测试并用 config.jsonc 控制用例集合
【免费下载链接】denoA modern runtime for JavaScript and TypeScript.项目地址: https://gitcode.com/GitHub_Trending/de/deno
Deno 的node:*兼容层有两套容易混淆的测试(doc/testing.md 中专门区分了它们):
tests/unit_node/— Deno 自己编写的node:*内置模块单元测试,通过cargo test unit_node::<module>运行;tests/node_compat/— Node.js 自己的测试文件,直接在 Deno 上执行来度量兼容性,运行哪一批用例由tests/node_compat/config.jsonc控制。
本文讲的是后者:如何在 Deno 源码仓库中运行 Node.js 官方测试用例,以及如何用config.jsonc增删用例、标记 flaky、按平台跳过或配置期望失败。适用前提是你在 Deno 仓库内做兼容性相关的开发或验证(需要先构建出仓库自带的 deno 二进制)。
准备条件
- 按
.github/CONTRIBUTING.md的 "building from source" 一节安装 Rust 工具链及原生编译依赖(编译器、cmake 等),并带子模块克隆仓库(CLAUDE.md 要求使用--recurse-submodules)。 - 构建开发版 deno:
cargo build --bin denonode compat 用例本体是 vendored 的 Node.js 测试套件,位于tests/node_compat/runner/suite/(tests/node_compat/README.md 说明它是denoland/node_test的 git 子模块),--recurse-submodules保证该目录有内容。
运行 node compat 测试
入口是tests/node_compat/mod.rs(README 称之为 "The script entrypoint of node")对应的node_compat测试目标。
运行单个用例
tests/node_compat/mod.rs 源码中的注释给出了标准形式,过滤词用"目录/文件名"(不含test/前缀,可写全名或片段):
# 过滤所有名字里带 test-assert 的用例 cargo test --test node_compat -- test-assert # 调试某个用例:让 deno 停在断点处等待调试器 cargo test --test node_compat -- test-assert --inspect-brktests/node_compat/README.md 中写的是简写形式cargo test <name of test file>,等价含义;--inspect-brk之外还支持--inspect-wait(见mod.rs的parse_cli_args)。
运行 config.jsonc 里登记的全部用例
doc/testing.md 指出./xhelper 封装了常用测试命令,node compat 对应:
./x test-compat <name>(运行./x --help可查看该 helper 封装的全部命令。)也可以走 tests/node_compat/runner/deno.json 里定义的testtask:
cd tests/node_compat/runner deno task test <过滤词>该 task 的实际内容是DENO_TEST_UTIL_DENO_EXE="$(deno eval 'console.log(Deno.execPath())')" cargo test --test node_compat --,即把当前 deno 可执行文件路径传给测试 harness 后再跑cargo test --test node_compat。
无过滤器时跑的是什么集合
这是理解config.jsonc作用的关键(mod.rs的main()):
- 不带过滤器:只运行
config.jsonc中登记过的用例; - 带过滤器:从完整 vendored 套件里运行所有匹配的用例,即使它没登记在
config.jsonc里; - 加
--report:不做 config 过滤,跑整个套件,且仅在 CI 环境(CI环境变量存在)下生成报告文件。
测试发现规则(collect_test_files_recursive):递归扫描runner/suite/test/下每个子目录,只收集文件名以test-开头、扩展名为.js/.mjs/.cjs/.ts的文件;以.开头的隐藏文件和fixtures、common、tools等IGNORED_TEST_DIRS目录会被跳过。
config.jsonc 字段:如何控制用例集合
tests/node_compat/config.jsonc的格式由 tests/node_compat/schema.json 定义,结构是{"tests": { "<目录/文件名>": { ... } }},键是相对runner/suite/test/的路径。每个条目支持的字段(README +mod.rs的TestConfig+ schema):
| 字段 | 类型 | 作用 |
|---|---|---|
(空对象{}) | — | 该用例登记为"应在 Deno 中通过",纳入 CI check |
ignore | boolean | 所有平台跳过;mod.rs要求配ignore: true必须写reason |
windows/darwin/linux | boolean 或期望失败对象 | 布尔值控制该平台是否运行(默认true);写成{"exitCode": N, "output": "pattern"}表示"运行但期望以该方式失败" |
linuxAarch64/linuxX86_64 | 同上 | 平台字段,按架构细分,覆盖linux |
flaky | boolean | 标记为 flaky,最多重试 3 次后才算失败 |
reason | string | 说明跳过/标记原因 |
env | object | 仅对该用例追加的环境变量,叠加在 runner 默认值之上;README 明确提示 sparingly 使用 |
exitCode/output | integer / string | 顶层期望失败配置,作用于所有平台(可被平台字段覆盖);output支持[WILDCARD] |
extraDenoArgs | string 数组 | 只对该用例追加的deno run/deno testCLI 参数 |
timeoutMs | number | 覆盖该用例的超时(毫秒);README/mod.rs 提示只用于本来就慢的用例,不要拿来掩盖挂起 |
仓库中的真实条目示例(摘自 tests/node_compat/config.jsonc):
// 普通登记:期望通过 "parallel/test-assert-async.js": {}, // 只跳过 Windows(文档明确建议用逐平台开关而不是笼统 ignore) "internet/test-dns-ipv6.js": { "windows": false }, // 全平台跳过,必须带 reason "abort/test-zlib-invalid-internals-usage.js": { "ignore": true, "reason": "Tests Node.js internal C++ binding (internalBinding('zlib').Zlib) which is not implemented in Deno" }, // 标记为 flaky "parallel/test-child-process-can-write-to-stdout.js": { "flaky": true }, // 单用例环境变量覆盖(该测试需要走 OpenSSL 分支,其他用例不能带这个变量) "parallel/test-crypto-rsa-dsa.js": { "env": { "DENO_INTERNAL_NODE_TEST_FORCE_SHARED_OPENSSL": "1" } }schema.json 里还给出期望失败的文档示例(平台字段写对象):"windows": { "exitCode": 1 }。语义(resolve_expected_failure/handle_expected_failure)是:该用例被运行,恰好以配置的方式失败(退出码相等、output的[WILDCARD]模式匹配)才算通过;如果它通过了,或失败方式不符,都记为失败,失败信息里会给出Exit code mismatch/Output mismatch detail的具体差异。
运行细节与结果验证
- 每个用例的实际执行:runner 用刚构建出的 deno 执行用例文件。用例源码中引用了
node:test时用deno test -A --quiet --unsafe-proto --no-check,否则用deno run -A --quiet --unsafe-proto;工作目录是runner/suite/,并自动注入NODE_TEST_KNOWN_GLOBALS=0、NODE_SKIP_FLAG_CHECK=1、NO_COLOR=1、TEST_SERIAL_ID等环境变量(mod.rs的TestSetup)。 - 用例内的
// Flags:行:runner 会解析它,把--expose-gc等 V8 标志转成--v8-flags=,把--no-warnings等 Node 选项放进NODE_OPTIONS,--inspect/--inspect=PORT会归一化成 Deno 的--inspect=127.0.0.1:PORT形式。 - 超时:默认 10 000 ms,macOS 上 20 000 ms,可用
timeoutMs逐用例覆盖。 - 串行目录:
sequential/和pummel/下的用例逐条串行执行(pummel 是 Node 的压力套件,会分配多 GB 缓冲区,并发跑会把 CI 机器拖垮)。 - PTY 用例:
pseudo-tty/前缀的用例需要平台支持 PTY,不支持时自动记为 ignored。 - flaky 处理:
flaky: true的用例最多跑 3 次;在 CI(IS_CI且非--report模式)下所有用例都按 flaky 处理。 - 验证方式:
- 单用例:
cargo test的输出显示ok/FAILED;失败时输出包含用例自身的 stdout/stderr(截断到 2000 字符),以及一行可复制的Command: NODE_TEST_KNOWN_GLOBALS=0 NODE_SKIP_FLAG_CHECK=1 NODE_OPTIONS='...' deno ...,按它可以直接复现失败(debugging_command_text)。 - 全量:非
--report模式下任何失败会触发panic_on_failures,即cargo test整体失败。 - 历史结果:tests/node_compat/README.md 提到有每日结果查看站点(node-test-viewer),可看全部用例的最新结果。
- 单用例:
- CI 行为:README 明确 "The items listed in
config.jsoncare checked in CI check" —— 你修好某个兼容问题、让新用例开始通过后,把它加进config.jsonc,它才会进入 CI 的必过集合;未登记的用例只在带过滤器的本地运行或--report模式下出现。
边界说明
doc/testing.md提醒:OS 相关跳过应使用逐平台开关(如"windows": false),而不是笼统的"ignore";ignore: true时reason是强制的(缺失时mod.rs会 panic)。config.jsonc里的键必须与runner/suite/test/下的相对路径一致(如parallel/test-foo.js),写错路径不会报错,只是该用例不会进入无过滤器运行集合。- 本文只覆盖 node compat 套件;
node:*内置模块的 Deno 自写单元测试在tests/unit_node/,用cargo test unit_node::运行,是另一个独立入口。
【免费下载链接】denoA modern runtime for JavaScript and TypeScript.项目地址: https://gitcode.com/GitHub_Trending/de/deno
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考