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

Jest测试中retryWhen未多次触发getUser调用的问题排查

问题原因分析

你遇到的核心问题是**retryWhen的工作机制理解偏差**:

  • 当myService.getUser返回的Observable抛出错误后,这个Observable就会终止。retryWhen操作符是监听上游的错误流,但它不会自动重新触发myService.getUser的调用——除非上游Observable被重新订阅。
  • 你当前的代码中,上游Observable只被订阅了一次,所以myService.getUser只会执行一次,后续retryWhen只是在错误流上重复处理,并不会再次调用原方法。

另外你的业务代码还有两个小问题:

  1. map里的return { data: data };中data未定义,应该是result
  2. retryWhen的终止条件逻辑错误:retryCount++ ===10会在第11次尝试时才抛出错误,实际重试次数会是10次,总调用次数为11次(初始1次+10次重试)

解决方案

1. 修正业务代码:用defer确保每次重试都重新调用getUser

使用rxjs的defer操作符包装myService.getUser,这样每次订阅(包括重试时的重新订阅)都会执行一次myService.getUser():

import { defer } from 'rxjs';

public getUser(userId: number, delayTime): Observable<any> {
    // 用defer包装,确保每次订阅都重新执行getUser调用
    return defer(() => this.myService.getUser(userId)).pipe(
      map((result: any) => {
        if (result) {
          return { data: result }; // 修正data未定义的问题
        }
        throw new Error('Result is empty');
      }),
      retryWhen((errors) => {
        let retryCount = 0;
        return errors.pipe(
          delay(delayTime),
          tap(() => {
            retryCount++;
            // 当重试次数达到10次时,抛出错误终止重试
            if (retryCount >= 10) {
              throw new Error('Max retries exceeded');
            }
          }),
        );
      }),
    );
  }

2. 修正测试代码:处理错误回调并验证调用次数

原测试只监听了next回调,但最终会抛出错误,需要添加error回调来完成断言,同时注意总调用次数是11次(初始1次+10次重试):

it('getUser throws error, getUser is called 11 times (1 initial + 10 retries)', (done) => {
      // mock service
      myService.getUser = jest.fn(() => throwError('mock error'));
      getUser(23, 1).subscribe({
        next: () => {
          // 不会走到这里,因为一直报错
          done.fail('Should not emit next value');
        },
        error: () => {
          expect(myService.getUser).toHaveBeenCalledTimes(11);
          done();
        }
      });
});

如果你的项目是Angular或支持fakeAsync,也可以用同步测试方式避免回调嵌套:

import { fakeAsync, tick } from '@angular/core/testing';

it('getUser throws error, getUser is called 11 times', fakeAsync(() => {
      myService.getUser = jest.fn(() => throwError('mock error'));
      let errorReceived = false;

      getUser(23, 100).subscribe({
        error: () => errorReceived = true
      });

      // 模拟等待所有重试延迟完成
      tick(100 * 10);
      tick(); // 等待最后一次错误抛出

      expect(errorReceived).toBe(true);
      expect(myService.getUser).toHaveBeenCalledTimes(11);
}));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 22:01:17