核心难点是异步执行时机、错误流向和上下文隔离;需聚焦执行顺序可控性、错误是否落地、测试环境匹配性三层排查,重点检查promise拒绝未捕获、测试未等待异步完成及断言时机错误。

JS 异步代码测试报错,核心难点不在语法,而在执行时机、错误流向和上下文隔离。很多报错看似是“undefined”或“cannot read property”,实际根源是异步任务未完成、Promise 被拒绝未捕获,或测试框架提前结束。快速排查需聚焦三个关键层:执行顺序是否可控、错误是否落地、测试环境是否匹配真实异步行为。
检查 Promise 拒绝是否被显式处理
测试中常见报错如 Unhandled promise rejection 或控制台红色警告,本质是 Promise 抛出错误但没被 .catch() 或 try/catch 捕获。Jest、Vitest 等现代测试框架默认会将未处理的 Promise 拒绝视为失败。
- 在
it或test中使用await时,必须包裹try/catch,不能只依赖外部catch - 避免写
promise.then(...).catch(...)后不返回该链——测试函数可能在 Promise 完成前就退出 - 若测试的是工具函数(如返回 Promise 的 fetch 封装),确保调用后有
await或return,否则测试框架无法等待其完成
确认测试是否等待了正确的异步节点
报错显示数据为 undefined、DOM 元素为空、状态未更新?大概率是测试没等异步操作真正落地。例如:
-
setTimeout或setInterval:Jest 提供jest.useFakeTimers()+jest.runAllTimers()主动推进时间 -
fetch或axios:必须 mock 网络请求(如jest.mock('node-fetch')或vi.mock('axios')),否则请求发不出,Promise 永远 pending - Vue/React 组件内
onMounted或useEffect中的异步逻辑:需用await waitFor(() => expect(...).toBeInTheDocument())(RTL)或await nextTick()(Vue Test Utils)等待 DOM 更新
警惕“同步假象”导致的断言时机错误
写 await fn(); expect(result).toBe(...) 却报错?注意:fn() 返回 Promise,但 result 可能是函数内部变量,而非 Promise 的 resolved 值。常见误写:
-
const res = myAsyncFn(); expect(res.data).toBe(...)→res是 Promise,res.data是undefined - 正确写法:
const res = await myAsyncFn(); expect(res.data).toBe(...) - 使用
async/await时,测试函数声明必须带async,且测试框架支持(Vitest 默认支持,Jest 需确保版本 ≥27)
统一处理全局未捕获异步错误(防漏网)
即使单个测试加了 try/catch,仍可能因事件监听器、定时器回调等产生“幽灵错误”。可在测试入口统一监听:
- Jest:在
setupFilesAfterEnv中添加process.on('unhandledRejection', ...)和window.addEventListener('error', ...) - Vitest:使用
globalSetup文件注册同上监听,并在afterAll中清理 - 监听到错误时,调用
fail()或抛出异常,强制测试失败,避免静默忽略











