异步测试难点在于时间控制与状态断言,需掌握三类模式:返回Promise、done回调、async/await;须mock外部依赖并覆盖异常流断言。

理解测试异步代码的本质难点
异步测试难,不在于语法,而在于时间控制和状态断言。JavaScript 单线程 + 事件循环机制意味着:你写的 expect 断言可能在异步操作完成前就执行了,结果永远是 undefined 或初始值。关键不是“怎么写”,而是“怎么等”——等 Promise 解决、等回调触发、等定时器结束、等 DOM 更新完成。
掌握三类主流异步测试模式
现代测试框架(如 Jest、Vitest)已内置支持,但需明确使用场景:
-
直接返回 Promise:测试函数本身返回 Promise,直接 return 它,框架自动等待完成
✅ 适用:fetch 封装、service 层函数、async 工具函数
❌ 不适用:含 setTimeout/setInterval 的副作用逻辑 -
使用 done 回调:在回调函数中调用 done() 显式通知测试结束
✅ 适用:传统 Node.js 风格回调(err, data)、事件监听器注册后手动触发 -
await + async test 函数:最推荐方式,语义清晰、错误堆栈完整、可配合 try/catch 精准断言拒绝态
✅ 适用:95% 的现代异步逻辑,包括 await 多个步骤、条件分支中的异步路径
精准模拟与隔离外部依赖
真实网络、定时器、DOM 操作会破坏测试稳定性与速度。必须用可控替代品:
-
Mock fetch / axios:用 Jest.mock() 或 msw(Mock Service Worker) 拦截请求,返回预设响应或模拟失败
避免因网络波动、API 变更导致测试随机失败 -
替换定时器:调用 jest.useFakeTimers(),再用 jest.runAllTimers() 或 jest.advanceTimersByTime(1000) 主动推进时间
不用傻等 3 秒,也能验证防抖、轮询、延迟加载逻辑 -
绕过真实 DOM 更新:对 React/Vue 组件测试,用 act() 包裹异步触发行为(如点击后发起请求),确保所有更新同步完成再断言
否则可能遇到 “Warning: An update inside a test was not wrapped in act(...)”
覆盖边界与异常流的断言技巧
只测“成功路径”远远不够。高阶测试要主动构造失败场景并验证处理逻辑:
- 用 mockRejectedValue() 让 Promise 拒绝,再 await expect(...).rejects.toThrow() 或检查 error message
比 try/catch + expect(err) 更简洁可靠 - 对 Promise.all 场景,用 Promise.allSettled 模拟部分失败,验证降级逻辑是否生效
- 测试超时逻辑时,不要依赖真实等待,而是 mock 一个长期 pending 的 Promise,并验证是否触发了 timeout 分支
- 检查未捕获拒绝(unhandledRejection):Jest 中可监听 process.on('unhandledRejection') 并断言是否被正确拦截


















