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

Angular 15中如何用Jest测试switchMap等RxJS操作符?

问题分析

你的测试虽然通过,但未覆盖tap、filter、take和switchMap的核心原因有三点:

  1. 你订阅的是原始的refreshTokenSubject$,而非handleRefresh()返回的经过管道处理的Observable
  2. BehaviorSubject初始值为null,被filter((token) => token)直接过滤,导致后续操作符从未执行
  3. handleRefresh()方法缺少必要的参数(req和next),且你未订阅该方法返回的Observable

修正后的拦截器代码

首先修复handleRefresh的语法错误和参数缺失问题:

private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
private refreshTokenSubject$ = this.refreshTokenSubject.asObservable();

// 补充req和next参数,修正filter的语法错误
handleRefresh(req: HttpRequest<any>, next: HttpHandler) {
  return this.refreshTokenSubject$.pipe(
    tap(() => console.log('refresh token')),
    filter((token) => token),
    take(1),
    switchMap((token) => {
      return next.handle(this.addToken(req, token));
    })
  );
}

// 假设addToken是添加token到请求的方法
private addToken(req: HttpRequest<any>, token: string): HttpRequest<any> {
  return req.clone({
    setHeaders: { Authorization: `Bearer ${token}` }
  });
}

覆盖所有RxJS操作符的Jest测试
beforeEach(() => {
  interceptor = TestBed.inject(RefreshTokenInterceptor);
  requestMock = new HttpRequest("GET", "/fakeRequest");
});

it('should trigger tap, filter, take and switchMap operators correctly', () => {
  const mockToken = 'valid-refresh-token';
  // 模拟addToken返回带token的请求
  interceptor['addToken'] = jest.fn().mockReturnValue(requestMock);
  
  const next = {
    handle: jest.fn().mockReturnValue(of())
  } as HttpHandler;

  // 监听console.log,验证tap执行
  const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();

  // 订阅handleRefresh返回的Observable
  const handleRefresh$ = interceptor['handleRefresh'](requestMock, next);
  const subscription = handleRefresh$.subscribe({
    complete: () => {
      // 验证take(1)是否完成了Observable
      expect(subscription.closed).toBe(true);
    }
  });

  // 先发送null,验证filter过滤无效token
  interceptor['refreshTokenSubject'].next(null);
  expect(next.handle).not.toHaveBeenCalled();

  // 发送有效token,触发所有操作符
  interceptor['refreshTokenSubject'].next(mockToken);

  // 验证各操作符的执行
  expect(consoleLogSpy).toHaveBeenCalledWith('refresh token'); // tap执行
  expect(interceptor['addToken']).toHaveBeenCalledWith(requestMock, mockToken); // switchMap中调用addToken
  expect(next.handle).toHaveBeenCalledWith(requestMock); // switchMap中调用next.handle
  expect(subscription.closed).toBe(true); // take(1)完成订阅

  // 清理间谍和订阅
  consoleLogSpy.mockRestore();
  subscription.unsubscribe();
});

测试要点说明
  • tap:通过监听console.log验证其执行
  • filter:先发送null验证无效token被过滤,再发送有效token验证通过
  • take(1):通过检查订阅是否关闭,验证Observable在一次有效发射后完成
  • switchMap:验证addToken和next.handle是否被正确调用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:07:13