JS单元测试报错多因测试节奏与执行不匹配,需按五类失败表现分类排查:断言失败、异常抛出、超时、编译加载失败、偶发飘红;异步测试聚焦Promise等待、错误捕获和断言时机;Mock需检查调用位置、返回值匹配及作用域隔离;调试优先分析日志前三行定位问题源头。

JS 单元测试报错,多数不是代码写错了,而是测试行为和实际执行节奏没对齐。快速排查的关键是先分类、再聚焦、最后验证——不翻业务逻辑,先看错误“长相”。
看懂失败类型,选对排查方向
测试失败不是一种情况,而是五种典型表现,每种对应不同根因:
- 断言失败(expected 5, got 4):大概率是业务逻辑变了、Mock 数据没更新,或测试数据非唯一(比如时间戳、随机 ID)
- 异常抛出(TypeError / ReferenceError):被测函数里真有空值、未定义变量、类型误用,或依赖没 mock 好(比如没注入 service 实例)
- 超时失败(Test timeout of 5000ms exceeded):异步没等完就断言,常见于未 await Promise、没处理定时器、fetch 没 mock 导致 pending
- 编译/加载失败(Cannot find module):路径改了但测试里引用没同步、别名配置漏加、ESM/CJS 混用导致解析失败
- 偶发飘红(本地绿,CI 红;单跑绿,全量红):测试间共享状态(如全局变量、localStorage、mock 的计数器)、随机性未固定、或顺序依赖未清理
异步测试失败的三大高频坑
JS 异步测试出问题,八成卡在这三个点上:
-
Promise 没被等待:写了
myAsyncFn()但没await或return,测试框架根本不知道要等它 -
错误没被捕获:Promise reject 后没
.catch()或try/catch,Jest/Vitest 默认视为失败,控制台还会报Unhandled promise rejection -
断言时机不对:比如
const res = apiCall(); expect(res.data).toBe(...)——res是 Promise,res.data必然是undefined;必须写成const res = await apiCall();
Mock 失效或污染的自查清单
Mock 不起作用,常因“看着像 mock 了,其实没生效”:
- 检查
jest.mock()或vi.mock()调用位置:必须在import之前,或放在文件顶部(ESM 下尤其敏感) - 确认 mock 返回值是否匹配调用方式:比如 mock 了
axios.get,但代码里调的是axios({ method: 'get', url: ... }),两者不等价 - 留意 mock 的作用域:用
beforeEach(() => { jest.clearAllMocks() })防止上一个测试的 mock 行为影响当前用例 - 组件测试中,DOM 更新未等到位就断言:React 用
await waitFor(() => expect(...)),Vue 用await nextTick()或await flushPromises()
调试时优先看的三行信息
别急着跳进代码,先读失败日志最上面三行:
- 第一行:失败的断言语句位置(哪个 test 文件、第几行),确认你跑的是目标用例
-
第二行:错误类型(
AssertionError/TypeError/Timeout),直接决定排查路径 -
第三段调用栈中第一个业务代码路径:比如
at calculateTotal (utils.js:12:15),说明问题从这里开始传导,顺藤摸瓜比盲猜高效得多











