为什么你的 Node 测试会卡死skills 项目教你诊断 flaky 测试与悬挂进程的完整指南【免费下载链接】skillsMy own collection of skills for modern Node.js development项目地址: https://gitcode.com/gh_mirrors/skills15/skillsskills是一个面向现代 Node.js 开发的技能与最佳实践合集其中内置了两份急救手册一份教你快速揪出时灵时不灵的 flaky 测试另一份教你定位跑完不退出的悬挂进程。如果你的node --test经常莫名其妙卡住、CI 频频超时或者同一个测试本地能过、流水线就挂这篇文章就是为你准备的 什么是 flaky 测试为什么它最致命Flaky 测试指没有任何代码改动却时而通过、时而失败的测试。它的危害不是报错而是不靠谱团队逐渐不再相信测试报告红灯被习惯性忽略每次失败都要排查半天最后发现是环境抖动调试时间被白白消耗真正的 Bug 更容易藏在这种噪音里而测试卡死进程不退出、CI 超时往往是同一类根因的极端表现——有资源被打开却从未被关闭。skills 项目的 skills/node/ 技能专门覆盖了这两大场景核心诊断规则在flaky-tests.md识别与诊断 flaky 测试stuck-processes-and-tests.md诊断不退出、卡住的进程与测试悬挂 Node 测试的 3 步诊断法skills 项目给出一套固定顺序的排查路径先隔离再失败得快最后抓未关闭句柄。第一步加超时 报告器让卡死显形默认的测试输出往往看不出卡在哪里。给node --test加上显式超时和 spec 报告器卡住时会直接告诉你文件路径和行号node --test --test-reporterspec --test-timeout15000超时后你会看到类似 test timed out after 15000ms 的错误并附带测试名和位置第一步的定位就完成了。第二步从整个测试集缩到单个文件、单个用例用文件路径缩小范围再用--test-name-pattern精确到某个用例node --test --test-timeout15000 path/to/file.test.ts node --test --test-timeout15000 --test-name-patternshould close resources path/to/file.test.ts单独跑能过、全量跑就挂本身就是一条重要线索说明存在测试间共享状态或顺序依赖。第三步用 why-is-node-running 抓赖着不走的句柄这是诊断悬挂进程最关键的一步。运行node --import why-is-node-running/include --test path/to/file.test.ts然后在另一个终端对打印出的 PID 发送kill -SIGUSR1 pid工具会立刻把仍然活跃、阻止进程退出的句柄handle及它们的创建位置dump 出来——通常是没关的 HTTP 服务器、没停的setInterval、没断开的数据库连接。安装提示现代 ESM 项目用why-is-node-running老 CommonJS 项目装 v2 版本即可它只用于诊断CI 里建议用环境变量开关控制输出。让测试卡死/失败的 7 大常见根因结合 flaky-tests.md 的归纳Node 测试出问题基本逃不出这 7 类#根因典型症状1用setTimeout猜等待时间本地过、CI 挂随机失败2依赖真实时间new Date()跨天、跨月、换时区就失败3硬编码端口并行跑测试时报EADDRINUSE4测试间共享模块级状态单跑全过一起跑就挂5依赖测试执行顺序加--parallel后失败6Promise 没被 awaitfire-and-forget测试通过但进程报错退出7资源没清理文件句柄、连接、定时器too many open files、进程挂起对应的修复思路skills 项目都给出了坏例子 vs 好例子的对照核心原则只有一句话等条件而不是等时间每个资源谁开的就在同一作用域里关。等待改用显式条件轮询而不是拍脑袋sleep 1000时间依赖改用t.mock.timers或固定日期端口改用port: 0让操作系统分配空闲端口共享状态放进beforeEach里重置数据库测试用事务 回滚保证隔离详见 testing.md 的Isolation and Independence章节一个真实的卡死案例服务器没关 定时器没停这是 CI 超时最经典的组合拳测试里起了 HTTP 服务器和一个setInterval但收尾一个都没做。skills 项目给出的确定性收尾写法见 stuck-processes-and-tests.md在t.after(...)里clearIntervalserver.close()后还要await once(server, close)确认真正关闭仓库里还附带了一份可直接参考的完整示例代码graceful-server.test.ts 展示了测试如何优雅地启动listen(0)随机端口并关闭服务器graceful-server.ts 则演示了生产环境中用closeWithGrace做优雅关闭——这两份文件都是正确关闭姿势的范本更多细节见 graceful-shutdown.md。修复完成的验收清单 ✅skills 项目强调没做完最后一步验证就不要宣布修好了。它的Definition of Done只有 4 条隔离出的复现用例连续跑 30 次不卡不挂全量node --test以退出码 0 结束CI 完整跑完且不再超时why-is-node-running不再报告意外的残留句柄CI 环境的额外注意资源更紧张CI 的 CPU/内存通常比本地小重计算用例单独给更长的timeout控制并发用--test-concurrency2这类参数控制并行度而不是放任全速mock 外部依赖外部 HTTP 调用一律用t.mock.method打桩断网、限流都不是你的锅快速上手从哪份文件开始看你的处境建议先读测试时过时不过找不到规律skills/node/rules/flaky-tests.mdnode --test跑完不退、CI 超时skills/node/rules/stuck-processes-and-tests.md想系统了解 node:test 的写法与 mockskills/node/rules/testing.md服务/测试收尾、信号处理skills/node/rules/graceful-shutdown.md了解 skills 项目全部技能README.md记住这套组合拳超时显形 → 逐层隔离 → 句柄 dump → 同域收尾 → 30 次回归。下次再遇到测试卡死别再盯着终端干等了 【免费下载链接】skillsMy own collection of skills for modern Node.js development项目地址: https://gitcode.com/gh_mirrors/skills15/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考