第 10 章 测试
以单元测试、工程自动化、基准、压力和业务数据转换建立正确性与性能回归证据。
直觉起点
、、、、共同构成本页的观察坐标。测试分离功能契约与性能预算:单元测试控制依赖和异步完成,基准测试隔离微小成本,压力测试在真实并发下观察吞吐、尾延迟和错误。 深入理解 Node 不是背诵 API,而是回答 JavaScript 与系统之间如何传递数据、谁拥有异步资源、错误沿哪条边传播、何时能安全退出。
六阶段执行链
1. 定义可观察契约
固定 Node 版本、依赖锁、入口参数、环境变量和输入,先预测同步栈、异步资源与最终输出。没有版本证据,就无法区分原书历史行为和现代运行时。
2. 隔离外部依赖
明确数据是模块标识、字节、事件、请求、消息还是制品,并标出创建资源与接收完成通知的所有者。资源创建必须同时设计错误、超时和关闭。
3. 控制异步完成
把正常路径压缩为最小可运行样本,只保留一个完成出口。回调、Promise、事件和进程消息不能为同一操作重复签发结果。
4. 建立性能基线
注入一个边界:空输入、分块、慢消费者、权限、断线、Worker 退出、依赖升级或部署失败。停止在首个偏离点并保存状态。
5. 施加真实压力
检查 2013 年接口在当前 Node 的状态:仍支持、语义变化、弃用或移出生态。现代替代保持原问题身份,同时明确迁移不能自动保证的行为。
6. 保存回归证据
删除故障并重放同一输入,等待文件、套接字、队列、Worker、日志和连接排空。输出、退出码与资源计数共同收敛才算恢复。
核心机制深挖
测试分离功能契约与性能预算:单元测试控制依赖和异步完成,基准测试隔离微小成本,压力测试在真实并发下观察吞吐、尾延迟和错误。 所有案例统一记录六类证据:输入表示、模块或协议边界、调度阶段、资源所有者、错误出口、关闭条件。表示错误会造成编码或分帧歧义;调度理解错误会把同步事件误认为异步回调;所有权不清会留下描述符、Worker 与连接;关闭条件缺失会让进程悬挂或强行丢数据。
本页签发不变量是:测试失败可重复,异步测试只完成一次,基准包含预热和统计,压力结果同时报告吞吐、分位延迟、错误率和资源上限。。代码必须回答正常路径完成什么、边界输入限制在哪里、失败由谁观察、取消怎样传播、关闭等待哪些资源,以及 2013 年接口在当前 Node 中是否仍适合新项目。成功输出不能独自证明这些条件。
2013 版与现代 Node 的版本账本
原书出版于 2013 年,覆盖早期 CommonJS、V8 分代回收、process.nextTick、setImmediate、node-heapdump、node-memwatch、旧 Buffer 构造器、cluster、node-inspector、AMD/CMD 和 CouchDB npm 仓库。重构保留 11 章 4 附录的知识身份,但现代示例采用安全且受支持的入口,并明确 node:* 模块、Promise/async、Buffer.alloc、N-API、Inspector、node:test、锁文件和当前供应链机制的对照关系。
版本账本至少包含原书接口、当时用途、当前支持状态、替代入口、行为差异和移除兼容层的条件。事件循环、TCP 无消息边界、字节编码、资源所有权等稳定原理不会因版本改变;模块解析细节、V8 回收器、默认超时、cluster 调度和包仓库实现必须按当前版本重新验证。
完整公开目录逐项讲解
第10章 测试
第10章 测试 要放回“定义可观察契约、隔离外部依赖、控制异步完成、建立性能基线、施加真实压力、保存回归证据”的完整运行链,先说明输入、资源、同步或异步边界,再说明错误与结束条件。
实验从“定义可观察契约”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.1 单元测试
异步单元测试必须返回 Promise 或明确完成信号,测试替身只隔离边界。基准要预热并多轮统计,压力测试报告吞吐、分位延迟、错误率与资源,而非只给平均值。
实验从“隔离外部依赖”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.1.1 单元测试的意义
异步单元测试必须返回 Promise 或明确完成信号,测试替身只隔离边界。基准要预热并多轮统计,压力测试报告吞吐、分位延迟、错误率与资源,而非只给平均值。
实验从“控制异步完成”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.1.2 单元测试介绍
异步单元测试必须返回 Promise 或明确完成信号,测试替身只隔离边界。基准要预热并多轮统计,压力测试报告吞吐、分位延迟、错误率与资源,而非只给平均值。
实验从“建立性能基线”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.1.3 工程化与自动化
异步单元测试必须返回 Promise 或明确完成信号,测试替身只隔离边界。基准要预热并多轮统计,压力测试报告吞吐、分位延迟、错误率与资源,而非只给平均值。
实验从“施加真实压力”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.1.4 小结
10.1.4 小结 要放回“定义可观察契约、隔离外部依赖、控制异步完成、建立性能基线、施加真实压力、保存回归证据”的完整运行链,先说明输入、资源、同步或异步边界,再说明错误与结束条件。
实验从“保存回归证据”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.2 性能测试
产品化证据包括可追溯制品、配置边界、滚动部署、回滚、结构化日志、指标和报警演练。优化必须保持正确性,异构服务通过版本化协议而非隐式共享状态协作。
实验从“定义可观察契约”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.2.1 基准测试
异步单元测试必须返回 Promise 或明确完成信号,测试替身只隔离边界。基准要预热并多轮统计,压力测试报告吞吐、分位延迟、错误率与资源,而非只给平均值。
实验从“隔离外部依赖”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.2.2 压力测试
异步单元测试必须返回 Promise 或明确完成信号,测试替身只隔离边界。基准要预热并多轮统计,压力测试报告吞吐、分位延迟、错误率与资源,而非只给平均值。
实验从“控制异步完成”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.2.3 基准测试驱动开发
异步单元测试必须返回 Promise 或明确完成信号,测试替身只隔离边界。基准要预热并多轮统计,压力测试报告吞吐、分位延迟、错误率与资源,而非只给平均值。
实验从“建立性能基线”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.2.4 测试数据与业务数据的转换
异步单元测试必须返回 Promise 或明确完成信号,测试替身只隔离边界。基准要预热并多轮统计,压力测试报告吞吐、分位延迟、错误率与资源,而非只给平均值。
实验从“施加真实压力”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.3 总结
10.3 总结 要放回“定义可观察契约、隔离外部依赖、控制异步完成、建立性能基线、施加真实压力、保存回归证据”的完整运行链,先说明输入、资源、同步或异步边界,再说明错误与结束条件。
实验从“保存回归证据”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
10.4 参考资源
10.4 参考资源 要放回“定义可观察契约、隔离外部依赖、控制异步完成、建立性能基线、施加真实压力、保存回归证据”的完整运行链,先说明输入、资源、同步或异步边界,再说明错误与结束条件。
实验从“定义可观察契约”开始:固定 Node 版本、入口、依赖锁、输入和资源上限,先预测执行顺序,再只改变一个边界条件。记录首个偏离点,恢复后重放同一输入并等待文件、套接字、Worker、日志和队列排空。
资源与失败矩阵
| 资源 | 正常完成 | 常见边界 | 失败出口 | 关闭证据 |
|---|---|---|---|---|
| 模块与包 | 导出一次、依赖锁定 | 循环依赖、平台差异 | 解析或 ABI 错误 | 模块图可追溯 |
| Buffer 与流 | 编码完整、背压生效 | 截断、跨字符、慢目标 | pipeline 拒绝 | 描述符关闭 |
| 套接字与请求 | 响应一次 | 半包、断线、超时 | error 或 abort | close 已观察 |
| Worker 与制品 | 退出、接管或回滚 | 崩溃、重启风暴 | 非零码与报警 | 在途请求排空 |
最小可运行实验
import assert from "node:assert/strict";
import test from "node:test";
test("async operation rejects once", async () => {
await assert.rejects(() => Promise.reject(new Error("expected")), {
message: "expected",
});
});book: 深入浅出 Node.js
edition: 2013-12
page: dnj-10-testing
sample: normal | boundary | failure | recovery
module_graph_fixed: true
resource_owner_known: true
single_completion: true
close_observed: truefreeze node version + dependency lock + input
predict sync stack + async resources + byte boundaries
change exactly one boundary condition
stop at the first divergent state
remove fault and replay the same sample
wait for files, sockets, workers, and logs to close常见误区与故障注入
四类样本与验收
| 样本 | 注入方式 | 必查证据 | 通过条件 |
|---|---|---|---|
| 正常 | 小输入、依赖健康 | 字节、事件、响应、提交 | 只完成一次 |
| 边界 | 空、长、分块、慢、并发 | 上限、背压、顺序 | 内存与延迟受控 |
| 失败 | 权限、断线、Worker退出、部署失败 | 首个错误与所有者 | 错误不丢不重复 |
| 恢复 | 删除故障后同样本重放 | close、exit、连接数、回滚 | 无悬挂资源 |
目录证据:第10章 测试、10.1 单元测试、10.1.1 单元测试的意义、10.1.2 单元测试介绍、10.1.3 工程化与自动化、10.1.4 小结、10.2 性能测试、10.2.1 基准测试、10.2.2 压力测试、10.2.3 基准测试驱动开发、10.2.4 测试数据与业务数据的转换、10.3 总结、10.4 参考资源。本页按完整公开目录独立教学重构,不复制原书正文;13 个条目全部进入输入、机制、失败、版本和关闭证据链。
练习
小结
- 单元测试:对应“定义可观察契约”的核心观察量。
- 测试替身:对应“隔离外部依赖”的核心观察量。
- 基准测试:对应“控制异步完成”的核心观察量。
- 压力测试:对应“建立性能基线”的核心观察量。
- 性能预算:对应“施加真实压力”的核心观察量。
- 13 个本页公开目录条目已全部映射到执行链与版本账本。
- 先预测再运行;最终输出、错误传播和资源关闭共同决定是否通过。