学JS异步测试关键在于明确“测什么”和“怎么等结果”,需用await/return Promise/done控制执行时机,结合mock、fake timers处理API、定时器、竞态等场景,并避免漏await、错mock、不清理等陷阱。

学 JS 异步代码测试,关键不是先背语法,而是搞懂“测什么”和“怎么等结果”。异步行为本身不可预测,测试时若不控制执行时机,用普通断言会直接失败——比如 expect(result).toBe('done') 在 Promise 还没 resolve 时就运行了。
理解异步测试的核心矛盾
同步测试默认“代码跑完就断言”,但异步操作的结果在将来才出现。测试框架(如 Jest、Vitest)必须提供机制让测试函数暂停执行,等异步逻辑完成后再继续校验。这背后依赖的是对 Promise 生命周期和事件循环的理解:微任务(Promise.then)比宏任务(setTimeout)优先执行,而测试框架的 await 或 done() 就是告诉它“别急着结束,我还在等”。
掌握三类主流写法
现代测试中,以下方式可混用,但推荐以 async/await 为主:
-
返回 Promise:测试函数直接 return 一个 Promise,框架自动等待它 settle(fulfill/reject)。适合简单链式调用,如
return fetch('/api').then(res => res.json()).then(data => expect(data.id).toBe(1)) -
使用 async/await:最直观,把异步逻辑当同步写。注意函数必须声明为
async,且所有 await 后的操作都需在 try/catch 中处理可能的 reject -
回调 done()(已逐步淘汰):传入
done参数,在回调末尾手动调用done()或done(err)。易遗漏、难调试,仅用于兼容老代码或特殊场景(如监听多次事件)
绕不开的典型场景与写法
真实项目中,你会频繁遇到这几类异步逻辑,对应测试要点不同:
-
模拟 API 调用:用
jest.mock('axios')或vi.mock('node-fetch')拦截请求,返回预设的 Promise.resolve({ data: ... }),避免发真实网络请求 -
定时器逻辑(setTimeout/setInterval):用
jest.useFakeTimers()控制时间流逝,再调用jest.runAllTimers()或jest.advanceTimersByTime(1000)触发回调,确保可预测 -
竞态条件(Race Conditions):比如多个请求中只取最快响应。用
Promise.race()模拟,并验证是否忽略慢响应;测试时需 mock 不同延迟,检查最终状态是否符合预期
避免常见陷阱
新手常踩的坑往往不是语法错,而是逻辑漏:
- 忘记
await或未 return Promise → 测试提前通过,实际异步逻辑根本没校验 - mock 返回的是普通对象而非 Promise → 异步流程被跳过,变成同步执行,掩盖问题
- 没清理定时器或事件监听器 → 影响后续测试,导致偶发失败(可用
afterEach(() => jest.clearAllTimers())) - 只测成功路径,忽略 reject 场景 → 加上
await expect(...).rejects.toThrow(...)或catch分支断言



















