JS异步测试难点在于环境与执行时序不匹配:未await异步操作、mock未正确返回Promise、忽略定时器/微任务执行顺序、未清理副作用。需用async/await、mockResolvedValue、fakeTimers、waitFor及afterEach清理。

JS 异步代码测试难,不是因为逻辑复杂,而是测试环境和执行时序不匹配 —— 比如你写了 await fetch(),但测试里没等它完成就断言;或者 mock 了接口返回,却忘了 resolve Promise,导致测试卡住或超时;又或者用了 setTimeout 或 setInterval,但 Jest 默认不处理定时器,结果测试跑完、异步回调还没触发,断言就全错了。
测试未等待异步操作完成
这是最常踩的坑:用 async/await 写业务逻辑,却在测试中当同步函数调用。
- 错误写法:
test('should load user', () => { const result = loadUser(); expect(result).toEqual(...); });——loadUser()返回 Promise,但没await,断言拿到的是 Promise 对象而非实际数据 - 正确做法:测试函数必须声明为
async,且await被测函数:test('should load user', async () => { const result = await loadUser(); expect(result.name).toBe('Alice'); }); - 补充提醒:Jest 中
test支持async,但若用done回调方式,容易漏掉done()调用,引发超时失败,建议统一用async/await
Mock 异步依赖时未正确 resolve/reject
mock 接口(如 fetch 或 axios)时,只改了函数引用,却没让它真正返回 Promise,测试会挂起或报错。
- 常见错误:
jest.mock('axios', () => ({ get: () => ({ data: { id: 1 } }) }));—— 这里get返回的是普通对象,不是 Promise,await axios.get()会直接 resolve 该对象,看似“成功”,实则掩盖了真实行为 - 正确 mock:
jest.mock('axios', () => ({ get: jest.fn().mockResolvedValue({ data: { id: 1 } }) }));或手动返回Promise.resolve(...) - 别忘测试异常路径:用
mockRejectedValue(new Error('Network error'))覆盖catch分支,验证错误提示、loading 状态是否正常切换
忽略定时器与微任务执行时机
涉及 setTimeout、setInterval、Promise.then、queueMicrotask 的逻辑,测试时容易因执行顺序错乱而断言失败。
- Jest 默认启用
fakeTimers,但需显式启用并控制:jest.useFakeTimers();后,用jest.runAllTimers()或jest.advanceTimersByTime(1000)主动推进 - 微任务(如
Promise.then)比宏任务(如setTimeout)优先执行。测试中若混合两者,需用await Promise.resolve()或await flushPromises()(自定义工具函数)确保微任务清空 - 小技巧:在断言前加
await new Promise(setTimeout)是反模式,应改用 Jest 提供的waitFor或act(React 测试)来等待状态更新
忘记清理副作用或全局状态
异步测试常伴随事件监听、轮询、缓存写入等副作用,若不重置,会影响后续测试。
- 比如测试中启动了
setInterval(() => update(), 5000),但没clearInterval,下次测试可能意外触发旧回调 - 解决方式:在
beforeEach初始化状态,在afterEach清理定时器、取消请求(AbortController)、重置 mock(jest.clearAllMocks()) - 对 React 组件测试,尤其注意
useEffect中的异步逻辑:务必在 cleanup 函数中取消 pending 请求或清除监听器,否则测试报 “Warning: Can’t perform a React state update on an unmounted component”


















