You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用jest.useFakeTimers仍遇轮询函数超时,求正确测试用例

轮询函数测试超时问题及解决方案

实现的轮询函数

export const wait = (ms: number): Promise<void> =>
  new Promise((resolve) => setTimeout(resolve, ms));

/**
 * @param fn 异步函数,返回待轮询的结果
 * @param cb 验证结果的回调,返回true时停止轮询
 * @param ms 轮询间隔时间
 * @returns 符合条件的结果
 */
export async function polling<T>(
  fn: () => Promise<T>,
  cb: (res: T) => boolean,
  ms: number
): Promise<T> {
  try {
    const res = await fn();
    if (cb(res)) {
      return res;
    }
    await wait(ms);
    return polling(fn, cb, ms);
  } catch (error) {
    throw error;
  }
}

我编写的测试用例

it('should poll for 5 times', async () => {
  jest.useFakeTimers();
  let count = 0;
  const fn = () => {
    count += 1;
    return count;
  };
  const cb = (res) => res === 5;
  const resP = polling(fn, cb, 1);
  jest.runAllTimers();
  const res = await resP;
  jestExpect(res).toBe(6);
});

遇到的超时错误

● Test Data Science Common Utility functions › should poll 5 times

    : Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:

       95 |   });
       96 | 
    >  97 |   it('should poll for 5 times', async () => {
          |   ^
       98 |     jest.useFakeTimers();
       99 |     let count = 0;
      100 |     const fn = () => {

问题分析与正确测试用例

你的测试用例存在3个核心问题导致超时:

  1. fn不是异步函数,但polling要求参数必须返回Promise,类型不匹配打乱了异步流程
  2. jest.runAllTimers()是同步执行逻辑,无法处理递归轮询产生的异步计时器链
  3. 断言逻辑错误:当res===5时回调返回true,轮询会立即停止,最终结果应为5而非6

以下是修正后的测试用例:

it('should poll until the result meets the condition', async () => {
  jest.useFakeTimers();
  let count = 0;
  // 修正:让fn返回Promise,符合polling的参数要求
  const fn = async () => {
    count += 1;
    return count;
  };
  // 当count等于5时停止轮询
  const cb = (res: number) => res === 5;
  const resP = polling(fn, cb, 100);

  // 使用runAllTimersAsync处理异步递归的计时器
  await jest.runAllTimersAsync();
  
  const res = await resP;
  // 断言最终结果是符合条件的5
  expect(res).toBe(5);
  // 额外验证:fn确实被调用了5次
  expect(count).toBe(5);

  // 测试完成后恢复真实计时器,避免影响其他测试
  jest.useRealTimers();
});

关键修改说明

  • 异步fn:必须将fn改为async函数,确保返回Promise,匹配polling的参数类型定义
  • runAllTimersAsync:递归轮询会不断生成新的异步计时器,该方法会等待所有异步计时器执行完成,解决超时问题
  • 修正断言值:回调返回true时,当前的res就是目标结果,所以最终返回值为5
  • 恢复真实计时器:测试结束后调用jest.useRealTimers(),避免fake timers干扰其他测试用例

内容的提问来源于stack exchange,提问作者bhushan laware

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 01:15:39