测试包含Promise与setTimeout的函数时Jest超时的原因是什么?
问题产生原因
- 核心逻辑顺序错误:你启用了Jest的假定时器
jest.useFakeTimers(),但在代码中先执行了await sleep(+SLEEP_DURATION),后调用jest.runAllTimers()。sleep函数内部的Promise依赖setTimeout的回调触发resolve,在假定时器模式下,你不主动调用跑定时器的方法,setTimeout的回调永远不会执行,导致sleep函数的Promise持续处于pending状态,测试阻塞,无论你把超时阈值设多大都会触发超时错误。 - 额外的错误点:
- 你没有给sleep函数创建Spy,直接调用
expect(sleep).toHaveBeenCalledWith会抛出断言错误,因为Jest没有监听过sleep的调用行为,且该断言属于冗余逻辑——你主动调用的sleep不需要额外校验调用行为。 - setTimeout的入参断言顺序错误:setTimeout第一个参数是要执行的回调函数,第二个参数才是等待时长,你的断言传入的参数顺序和实际入参不匹配。
- 你没有给sleep函数创建Spy,直接调用
修复方案
调整执行顺序,保证定时器在Promise等待前被触发,同时修复错误的断言逻辑即可,修复后的测试代码如下:
import { sleep } from "../../helpers/utils"; console.log = jest.fn(); jest.useFakeTimers(); test("calls sleep with correct argument and calls console.log", async () => { const NODE_ENV = "test"; const SLEEP_DURATION = 100; process.env = { ...process.env, NODE_ENV }; const timeoutSpy = jest.spyOn(global, "setTimeout"); // 调用sleep但不立即await const sleepPromise = sleep(SLEEP_DURATION); // 执行所有定时器回调 jest.runAllTimers(); // 等待Promise队列清空,sleep执行完成 await sleepPromise; // 校验setTimeout调用参数 expect(timeoutSpy).toHaveBeenCalledWith(expect.any(Function), SLEEP_DURATION); // 校验console.log调用 expect(console.log).toHaveBeenCalledWith("sleep end"); });
如果你使用的是新版Jest的modern假定时器配置,可以把jest.runAllTimers()替换为await jest.runAllTimersAsync(),兼容性更好。
内容的提问来源于stack exchange,提问作者ffx292
相关产品推荐
相关产品推荐

