Node.js 项目越往服务端和工具化走,测试越不是可有可无的附加项。真正难的不是“会不会写断言”,而是知道哪些地方该测、怎么隔离依赖、哪些场景要真实联动、哪些场景该 mock。这一篇会从测试分层开始把思路梳理清楚。
为什么先要分层,而不是上来就写用例?
因为不同类型的测试要回答的问题根本不同。你如果不先分清目标,就很容易出现两种情况:
- 所有测试都写得很重,执行慢、定位差;
- 所有测试都写得很轻,真正的协作问题根本测不到。
测试分层的本质,不是为了显得体系化,而是为了让每一层都只回答它最该回答的问题。
单元测试到底该测什么?
单元测试最适合验证那些职责清楚、依赖少、输入输出明确的逻辑。比如:
- 工具函数;
- 数据转换逻辑;
- 纯计算流程;
- 独立模块的边界行为。
它的价值不在于“覆盖率好看”,而在于当局部逻辑出问题时,你能快速知道是哪一块坏了。
这也是为什么单元测试最好尽量靠近“纯逻辑”和“明确边界”的位置。越是依赖数据库、网络、时间、随机数和外部状态的地方,单元测试就越容易为了隔离而变得脆弱。
集成测试为什么在 Node 项目里特别重要?
很多 Node 项目的问题,真正危险的地方不在单个函数,而在模块之间的协作处。比如:
- 服务层和数据层是否正确配合;
- 配置和中间件是否正确装配;
- 某个流程跨了数据库、缓存和外部依赖后是否还可靠。
这些问题靠纯单元测试通常很难覆盖。也正因为如此,集成测试在 Node 项目里往往非常重要。它帮助你确认“模块拼起来以后,整体是否仍然工作正常”。
尤其在服务端项目中,很多真正会出事故的问题都发生在组合边界上,而不是某个单一函数。配置装配顺序、依赖注入、数据库事务、缓存读写、外部服务超时,这些都更适合在集成层暴露。
接口测试和端到端思维在验证什么?
当项目进入服务端形态后,HTTP 层本身也应该被认真验证。接口测试真正关心的不是内部代码结构,而是:
- 路由是否存在;
- 状态码是否符合约定;
- 输入校验是否正确;
- 响应结构是否稳定;
- 错误路径是否按契约返回。
这类测试能帮助你站在消费者角度验证系统,而不是只站在实现者角度看局部代码。
它的另一个价值是保护契约。只要接口已经被前端、第三方系统或自动化任务依赖,那么状态码、字段结构、分页包裹和错误格式就都不应该轻易漂移。接口测试正是在帮你盯住这些“实现内部改了,但对外不能乱”的边界。
mock 到底该怎么用?
mock 的价值在于控制边界、隔离不稳定依赖和提升测试效率。但它的风险也很明显:如果你把所有外部协作都 mock 掉了,最后得到的可能是一套“在模拟世界里全部通过”的测试,而不是真实系统能用的证据。
所以,一个更稳妥的原则是:
- 该隔离的地方隔离;
- 该真实联动的地方真实联动;
- 不要为了写得快,把所有复杂度都从测试里删掉。
一个很实用的判断方法是:如果你这次测试要验证的重点就是某个外部依赖本身,那就尽量别 mock;如果你要验证的是当前模块在依赖出现某种返回或异常时的反应,那 mock 就很有价值。关键不在于“用不用 mock”,而在于它有没有帮助你更准确地回答当前测试问题。
一套更可靠的 Node 测试策略该怎么落地?
把这篇压缩成更可执行的原则,大致可以收敛成:
- 用单元测试守住纯逻辑和边界函数;
- 用集成测试验证模块协作和基础设施装配;
- 用接口测试守住对外契约;
- 用 mock 控制不稳定依赖,但不要把真实风险全部屏蔽掉;
- 让测试分层服务于定位效率,而不是只追求数字上的覆盖率。
总结
这一篇我们把 Node 项目的测试分层梳理清楚了:单元测试验证局部逻辑,集成测试验证模块协作,接口测试验证对外契约,而 mock 则需要在效率和可信度之间找到平衡。
下一篇会继续往工程深水区走,开始讨论 Node.js 中的性能和内存行为。