核心是用一个真实async函数贯穿单元测试、异步测试、Mock、覆盖率和断点调试:从用户ID获取、校验转换、early return到try/catch,按执行流顺序串联五大能力,以路径覆盖为目标,实现测试与调试互证闭环。

学 JS 单元测试、异步测试、Mock、覆盖率和浏览器断点调试,不靠堆资料,而靠抓主线、建闭环、练手感。核心是:用一个真实函数贯穿所有环节,边写边测边调,让每个技术点都落地到同一段代码上。
从一个真实 async 函数开始
别从“Hello World”起步,选一个带实际逻辑的函数,比如:
- 接收用户 ID,调用 axios.get 获取用户信息
- 对返回数据做字段校验和格式转换(如生日转成 YYYY-MM-DD)
- 成功时返回处理后的对象;失败时捕获错误,返回空对象或抛出业务异常
- 含 early return(如 ID 为空直接返回)和 try/catch 分支
这个函数天然覆盖了参数校验、异步请求、数据处理、错误路径、副作用——所有你要练的点都在里面。
按执行流顺序串起五大能力
不要割裂学“Mock 是什么”“覆盖率怎么开”,而是按代码运行顺序推进:
- 断点调试先行:在浏览器 DevTools 中打开该函数,打三个断点——入口处、await 后、catch 块内。手动触发,观察 call stack、scope 变量、Promise 状态变化。你会直观看到异步暂停点在哪、变量何时可用、错误如何冒泡
-
写第一个单元测试:用 Jest 写
test('fetchUser with valid id returns formatted user', async () => { ... })。注意必须加async,必须await调用,不能混用.then() -
立刻加 Mock:用
jest.mock('axios')或axios.get.mockResolvedValue(...)拦截真实请求。再补一个mockRejectedValue用例,确保 catch 块被执行 -
跑覆盖率看缺口:加
--coverage --collectCoverageFrom="src/**/*.{js,ts}",打开 HTML 报告。你会一眼看到 if 的 false 分支没走、try 没覆盖、early return 后的行灰了——这些就是你下一个测试要补的 - 反向验证调试能力:把刚补的失败用例在浏览器里再跑一遍,对比断点停在 catch 的时机、error 对象结构、是否被正确吞掉——测试和调试此时形成互证
Mock 不是“假装有返回”,而是控制分支开关
Mock 的本质是给被测函数制造确定性输入,从而驱动它走指定路径。重点不是“怎么 mock”,而是“为什么 mock 这个值”:
- mock
resolvedValue→ 验证正常流程、数据转换逻辑、后续 await 链 - mock
rejectedValue→ 触发 catch、空对象返回、错误日志等分支 - mock
mockImplementation(() => Promise.reject(new Error('timeout')))→ 测试特定错误类型处理(比如区分网络超时和 404) - 对依赖模块做
jest.mock('./utils')并重置函数实现 → 隔离纯函数副作用,比如防止 formatBirthday 修改全局 Date
覆盖率不是数字游戏,而是路径地图
Jest + Istanbul 统计的是语句(statement)、分支(branch)、函数(function)三类覆盖率。真正要盯的是:
- 分支未覆盖:if/else、try/catch、switch case 中某一分支从未执行。补一个让条件为 false 的测试即可
- 语句未覆盖:通常是 early return 后面的代码、throw 之后的行、或者 await 后面逻辑在 reject 时跳过了。检查是否漏了 reject 场景或边界输入
- 函数未覆盖:说明该函数根本没被任何测试调用过,可能被废弃,也可能只是没被集成进主流程
覆盖率报告不是终点,是下一条测试用例的待办清单。
断点调试和单元测试不是两套工具,是一体两面
你在 DevTools 里单步执行时看到的,就是测试里要模拟的;你在测试里覆盖不到的路径,就是调试时最该停下来多看两眼的地方:
- 测试中 mock 返回了空数组,但调试发现真实 API 有时返回 null —— 补一个
mockResolvedValue(null)用例 - 断点停在 await 后发现 response.data 是 undefined,但测试只 mock 了
{ data: {...} }—— 补一个 data 缺失的场景 - 测试全部通过,但调试时发现 console.warn 频繁触发 —— 加
jest.spyOn(console, 'warn').mockImplementation()捕获并断言
调试帮你发现“世界不按预期运行”,测试帮你锁定“哪些不预期必须被处理”。两者来回切,代码才真正稳。


















