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

Jest测试中无法监听函数第二次及第三次调用致超时失败

解决Jest测试中SpyOn监听重试函数超时问题

你的测试超时大概率不是SpyOn本身失效,而是重试逻辑的副作用或者定时器未处理导致的,以下是几个排查和修复方向:

1. 每次测试后清理Mock副作用

如果多个测试共用同一个Spy,前一个测试的Mock会影响后续测试,导致Mock的调用次数或返回值不符合预期。在测试块的afterEach钩子中重置所有Mock:

afterEach(() => {
  // 恢复所有被Spy的原始函数
  jest.restoreAllMocks();
});

或者针对单个Spy手动重置:

it("should succeed after retry on first attempt failure", async () => {
  const lockDoorsSpy = jest.spyOn(nukiService, "lockDoors")
    .mockResolvedValueOnce({ status: 503 })
    .mockResolvedValueOnce({ status: 200 });

  // ...测试逻辑

  // 测试结束后恢复原始函数
  lockDoorsSpy.mockRestore();
});

2. 处理重试逻辑中的定时器

如果lockDoorsWithRetry内部在重试前有延迟(比如setTimeout),Jest默认5秒的超时时间很容易被耗尽。解决方式有两种:

方式一:延长测试超时时间

在测试用例的第三个参数指定超时时间:

it("should succeed after retry on first attempt failure", async () => {
  // ...测试逻辑
}, 10000); // 延长到10秒

方式二:Mock定时器快速执行

用Jest的假定时器跳过实际等待:

it("should succeed after retry on first attempt failure", async () => {
  jest.useFakeTimers(); // 启用假定时器

  const lockDoorsSpy = jest.spyOn(nukiService, "lockDoors")
    .mockResolvedValueOnce({ status: 503 })
    .mockResolvedValueOnce({ status: 200 });

  // 先启动异步函数,不等待
  const retryPromise = nukiService.lockDoorsWithRetry();
  // 快进所有定时器,触发重试逻辑
  jest.runAllTimers();
  // 再等待Promise完成
  const result = await retryPromise;

  expect(result.status).toBe(200);
  expect(lockDoorsSpy).toHaveBeenCalledTimes(2);

  jest.useRealTimers(); // 恢复真实定时器
});

3. 简化Mock返回值写法

mockResolvedValueOnce本身会返回一个resolved状态的Promise,不需要额外包裹Promise.resolve,简化写法避免冗余:

// 错误写法
.mockResolvedValueOnce(Promise.resolve({ status: 503 }))
// 正确写法
.mockResolvedValueOnce({ status: 503 })

4. 验证Spy的调用情况

在测试中添加Spy的调用次数验证,确认Mock是否真的生效:

it("should fail after 3 retries", async () => {
  const lockDoorsSpy = jest.spyOn(nukiService, "lockDoors")
    .mockResolvedValueOnce({ status: 503 })
    .mockResolvedValueOnce({ status: 503 })
    .mockResolvedValueOnce({ status: 503 });

  const result = await nukiService.lockDoorsWithRetry();
  
  expect(result.status).toBe(424);
  expect(lockDoorsSpy).toHaveBeenCalledTimes(3); // 确认调用了3次
});

5. 检查重试逻辑的实现

如果以上步骤都没问题,那可能是lockDoorsWithRetry的重试逻辑有缺陷:

  • 确认是否正确捕获了503状态并触发重试
  • 检查重试的终止条件(比如是否限制了最多3次重试)
  • 确保异步逻辑没有出现未处理的Promise导致挂起

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 11:24:59