JS异步测试核心是让框架等待异步完成再断言,关键在控制执行时机、隔离副作用、覆盖成功与失败路径;需明确目标、选对工具(如async/await)、精准mock依赖、覆盖并发、串行、取消等典型场景。

JS 异步代码测试的核心,是让测试框架能“等”到异步操作完成再断言,而不是在 Promise 还没兑现、回调还没触发时就结束测试。关键不在写得多,而在控制好执行时机、隔离副作用、覆盖成功与失败路径。
明确测试目标:测什么不测什么
异步测试不是测 setTimeout 或 fetch 本身是否工作(那是浏览器/运行时的事),而是验证:
• 你的函数是否正确发起异步调用(如参数、URL、请求头)
• 是否在成功时按预期处理数据(如更新状态、调用回调)
• 是否在失败时妥善降级(如捕获错误、显示提示、重试逻辑)
• 是否避免重复请求、内存泄漏或未清理的定时器
选对测试工具与模式
现代测试框架(Jest、Vitest、Jasmine)原生支持 async 测试,无需手动写 done() 回调:
• 直接返回 Promise:test('fetches user', () => expect(fetchUser(1)).resolves.toEqual({...}))
• 使用 async/await:test('handles error', async () => { await expect(fetchUser(999)).rejects.toThrow() })
• 避免 .then().catch() 嵌套——它会让错误堆栈变浅、断言位置模糊
精准模拟异步依赖
真实网络、定时器、文件系统必须 mock,否则测试慢、不稳定、不可控:
• fetch / axios:用 Jest.mock('node-fetch') 或 MSW(Mock Service Worker)拦截请求,返回预设响应或延迟
• setTimeout / setInterval:用 jest.useFakeTimers() + jest.runAllTimers() 或 jest.advanceTimersByTime(ms) 控制时间流
• 第三方 SDK(如 Firebase、Stripe):用 jest.mock('@firebase/app') 模拟模块导出,只暴露你用到的方法
• 切忌只 mock 返回值却不 mock 执行时机——比如 mock 一个立刻 resolve 的 Promise,就测不出竞态问题
覆盖典型异步场景
光测“一次成功”远远不够,要主动构造边界情况:
• 并发请求:用 Promise.all 同时发起多个请求,验证是否全部完成、是否有竞态覆盖
• 串行依赖:先调 A 再用 A 结果调 B,mock A 成功但 B 失败,检查错误是否被正确传递
• 中断与取消:用户快速切换页面时,AbortController 是否被传入 fetch、Promise 是否被正确中止
• 加载状态:组件中 loading = true → 请求开始,loading = false → 成功/失败后,用 waitFor 或 act 等待 DOM 更新


















