学JS单元测试的关键是建立“可验证→可隔离→可追踪”闭环习惯:从编写可测函数(输入确定、输出稳定、副作用分离)开始,用Vitest写测试,规范异步处理与Mock,并通过断点调试提升问题定位效率。

学 JS 单元测试、异步测试、Mock 和断点调试,关键不是堆工具,而是建立「可验证 → 可隔离 → 可追踪」的闭环习惯。下面按真实开发节奏组织,直接上手就能用。
从一个可测函数开始写测试
先确保被测代码本身“友好”:输入确定、输出稳定、副作用分离。比如避免直接调用 Date.now() 或 fetch,把它们抽成参数或依赖项。
- 函数只做一件事,比如
formatPrice(amount, currency),不顺便发日志或改 localStorage - 异步操作统一用
async/await封装,返回 Promise,方便在测试里await - 导出要测的函数(不要只在内部使用),且文件命名规范,如
utils.js对应utils.test.js
用 Vitest 写第一个真正有效的测试
Vitest 是当前最顺手的选择,尤其在 Vite 项目中——零配置起步,API 和 Jest 高度一致,学一个等于掌握两个。
- 安装:
npm install -D vitest,并在package.json加脚本:"test": "vitest" - 测试文件里用
describe描述模块,it描述具体行为,expect做断言 - 别写“调用完就完事”的测试,每个
it必须有明确预期,比如:expect(parseDate('2023-01-01')).toEqual(new Date(2023, 0, 1))
异步逻辑怎么测才不翻车
Promise、setTimeout、async 函数不能靠 console.log 看,必须让测试等待结果落地。
- 测试
async函数:直接await调用,再断言,Vitest 自动处理 Promise 生命周期 - 测试定时器(如防抖):用
vi.useFakeTimers()控制时间流,vi.advanceTimersByTime(300)快进,不用真等 - 测试失败路径:用
await expect(...).rejects.toThrow(...)验证异常是否如期抛出
Mock 不是“造假”,是“划清边界”
Mock 的核心目标是让测试只聚焦被测单元本身,不被外部环境干扰。它不是掩盖问题,而是防止问题扩散。
- 模拟函数行为:用
vi.fn()创建替身,设定.mockReturnValue()或.mockImplementation() - 模拟整个模块:用
vi.mock('./api.js')替换真实请求模块,返回预设响应数据 - 每次测试后清理:加
afterEach(() => vi.clearAllMocks()),避免 mock 状态跨测试污染
断点调试不是“最后手段”,而是日常读逻辑的放大镜
在测试代码里打断点,比在业务代码里更高效——你清楚知道输入、环境、执行路径,问题定位快得多。
- 确保
vitest.config.ts中sourceMap: true(Vite 默认开启),否则断点无法命中 - 在
expect前一行打点,右键选择 Debug Test,VS Code 会自动启动调试会话 - 配合浏览器 DevTools 的 Sources 面板:可单步(F10)、步入函数(F11)、跳出函数(Shift+F11)、查看作用域变量











