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

如何测试带延迟的递归HTTP轮询超时场景?

解决轮询超时场景的测试问题

我来帮你搞定这个超时场景的测试难题~你遇到的expected no open requests found 1错误,本质是超时场景需要触发多次轮询请求,但之前的测试只处理了单次请求,导致还有未完成的HTTP请求挂着,最终触发了未处理请求的校验错误。

核心思路

你的poll函数是递归实现的:每次返回Ongoing状态时,会延迟retryTime后再次发起GET请求,直到重试次数耗尽才抛出超时错误。所以测试需要模拟以下流程:

  1. 触发初始的POST请求,获取actionId。
  2. 循环模拟config.retryCount + 1次GET请求(因为初始retryCount是配置值,每次调用poll会先发起请求再减计数,直到计数为-1时抛出错误)。
  3. 每次请求后触发tick(config.retryTime)来模拟延迟,触发下一轮轮询。
  4. 最终验证超时错误是否正确抛出,且所有HTTP请求都被处理完毕。

完整测试用例代码

it('should throw timeout error when action stays ongoing beyond retry limit', fakeAsync(() => {
  // 准备Mock数据:所有轮询请求都返回Ongoing状态
  const mockOngoingResponse = { action: { id: mockActionId, state: 'Ongoing' } };
  let timeoutErrorTriggered = false;

  // 触发doAction并订阅结果,捕获超时错误
  service.doAction(mockContent).subscribe(
    () => fail('Expected timeout error, but received success response'),
    (err) => {
      expect(err).toEqual('Action Timed Out');
      timeoutErrorTriggered = true;
    }
  );

  // 处理第一个POST请求(获取actionId的请求)
  const postCall = httpTestingController.expectOne(url1);
  expect(postCall.request.method).toEqual('POST');
  postCall.flush(mockPostActionResponse);

  // 处理所有轮询的GET请求:总次数为 config.retryCount + 1
  const totalPollAttempts = config.retryCount + 1;
  for (let i = 0; i < totalPollAttempts; i++) {
    // 模拟轮询的延迟时间
    tick(config.retryTime);
    // 匹配当前的GET请求(多次请求URL相同,用match获取所有匹配项)
    const allGetCalls = httpTestingController.match(`${url2}/${mockActionId}`);
    const currentGetCall = allGetCalls[i];
    expect(currentGetCall.request.method).toEqual('GET');
    // 返回Ongoing状态,触发下一轮轮询(直到最后一次)
    currentGetCall.flush(mockOngoingResponse);
  }

  // 验证超时错误是否正确触发
  expect(timeoutErrorTriggered).toBeTrue();

  // 确保所有HTTP请求都被处理完毕,避免未处理请求的错误
  httpTestingController.verify();
}));

关键细节说明

  1. 为什么用match而不是expectOne:因为轮询请求的URL完全相同,expectOne只能匹配单次请求,而match会返回所有匹配该URL的请求数组,我们可以按顺序逐个处理。
  2. 循环次数的计算:初始retryCount是config.retryCount,每次进入poll函数后会先发起GET请求,再将计数减1,直到计数小于0时抛出错误。所以总共有config.retryCount + 1次GET请求。
  3. tick的时机:每次处理完GET请求后,必须用tick(config.retryTime)来模拟time(config.retryTime)的延迟,这样才能触发下一轮的轮询调用。
  4. 最后的verify():必须调用这个方法来确保所有HTTP请求都被flush,彻底解决你遇到的“未处理请求”错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:33:17