单元测试应与被测代码同级存放,命名需明确“谁在什么情况下会怎样”;按功能模块组织目录,避免扁平结构;测试分单元、模块、契约三层,覆盖不同粒度。

在大型 JavaScript 项目中,单元测试的目录结构和命名规范不是辅助项,而是可维护性的基础设施。结构混乱会让测试难以定位、更新和信任;命名模糊则会让失败报告失去诊断价值。核心原则就两条:测试文件紧贴被测代码、测试名称讲清“谁在什么情况下会怎样”。
测试文件与源码同级或就近存放
避免把所有测试塞进 /test 或 /__tests__ 根目录。大型项目里,最有效的做法是让测试文件与被测模块物理上靠近:
- 每个功能模块目录内直接放
.spec.js或.test.js文件,例如:src/user/user.service.js对应src/user/user.service.spec.js - 若使用 Jest,默认识别
.spec.js和.test.js后缀,无需额外配置 - 不强制要求
__tests__目录,除非团队明确约定——但该结构会割裂业务逻辑与验证逻辑 - 工具类、工具函数等通用模块,测试文件放在同一层级,如
src/utils/date-format.js→src/utils/date-format.spec.js
按功能模块组织测试目录,而非按测试类型
拒绝 “所有测试用例都归到 /test/unit/” 这种扁平结构。它在项目初期看似简洁,但随着模块增多,user.test.js、product.test.js、auth.test.js 全堆在一起,根本无法体现业务边界。
- 推荐结构示例:
src/product/下包含product.service.js、product.dto.js、product.service.spec.js、product.dto.spec.js - 模块入口文件(如
index.js)也应有对应集成级测试,比如product/index.spec.js验证模块整体导出是否正确 - 组件库项目还可加
__snapshots__子目录存放 Jest 快照,与组件同级
测试名称必须包含三个要素
一个合格的 it() 描述,应该让 QA、DevOps 或半年后的你一眼看懂问题所在,无需打开代码。名称需同时说明:
-
被测对象:方法名或行为主体,如
UserService.createUser -
执行场景:输入条件、前置状态或异常触发点,如
when email is already taken -
预期结果:返回值、抛出错误或状态变更,如
then throws ConflictError
组合起来就是:it('When email is already taken, then throws ConflictError', () => { ... })。这种写法天然支持需求对齐,也便于自动生成测试报告摘要。
分层编写测试,覆盖不同粒度
大型项目不能只靠“能跑就行”的测试。要分层建设,每层承担不同职责:
-
单元层:单个函数或方法,隔离依赖(用 Jest Mock),验证逻辑分支与边界值(
null、空数组、超长字符串等) - 模块层:多个协作类/函数组成的最小业务单元,例如“用户注册流程”,验证内部调用顺序与错误传播
- 契约层(可选):对接口 DTO、事件 payload 等做结构校验,确保前后端或微服务间数据格式一致
每一层的测试文件可共存于同一模块目录下,用文件名区分粒度,如 user.service.spec.js(单元)、user.registration-flow.spec.js(模块)、user.dto.spec.js(契约)。


















