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

Angular全局HTTP拦截器单元测试求助:POST请求自动添加XSCRF Token功能测试未通过

解决全局HTTP拦截器单元测试失败的问题

我帮你梳理下测试用例失败的核心问题,以及对应的修复方案:

首先,先修正业务代码里的小错误

你的optout.service.ts里getReponse()拼写错误,应该是getResponse(),否则调用时会直接报错:

// 修正后的OptoutService方法
getResponse(): Observable<optout> {
  return this.httpClient.get<any>(`https://thisIsGetTokenApi.com`).pipe(
    mergeMap(token => this.httpClient.get<optout>(`https://someurlenpoint2.com`))
  );
}

postResponse(data: optout): Observable<optout> {
  return this.httpClient.post<optout>('https://api-endpoint', data);
}

测试用例的核心问题及修复

你的测试用例存在几个关键问题,导致断言始终失败:

1. 未处理异步流的时序问题

调用optoutService的方法都是异步操作,必须用fakeAsync和tick()等待所有异步操作完成,否则断言会在拦截器处理响应、存储Token前就执行。

2. 未正确模拟两个请求的响应

getResponse()通过mergeMap链式调用了两个接口,需要分别模拟这两个请求的响应——尤其是第一个获取Token的请求,要返回包含XCSRFTOKEN的响应体,这样拦截器才能正确存储Token。

3. 断言对象错误

你现在断言的是获取Token的请求头,但拦截器的逻辑是在POST请求时添加X-CSRF-TOKEN头,应该验证POST请求的头是否正确。

4. 未定义测试变量

测试里的getTokenApi变量未声明,需要明确对应到获取Token的接口地址。


修正后的完整测试代码

import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { GlobalHttpInterceptorService } from './global-http-interceptor.service';
import { RouterTestingModule } from '@angular/router/testing';
import { OptoutService } from './optout.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { optout } from '../model/optoutModel';

describe('GlobalHttpInterceptorService', () => {
  let service: GlobalHttpInterceptorService;
  let optoutService: OptoutService;
  let httpMock: HttpTestingController;
  // 定义测试用的API地址常量
  const GET_TOKEN_API = 'https://thisIsGetTokenApi.com';
  const GET_FORM_API = 'https://someurlenpoint2.com';
  const POST_API = 'https://api-endpoint';

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [RouterTestingModule, HttpClientTestingModule],
      providers: [
        OptoutService,
        {
          provide: HTTP_INTERCEPTORS,
          useClass: GlobalHttpInterceptorService,
          multi: true
        }
      ]
    });
    service = TestBed.inject(GlobalHttpInterceptorService);
    optoutService = TestBed.inject(OptoutService);
    httpMock = TestBed.inject(HttpTestingController);
  });

  afterEach(() => {
    // 确保所有HTTP请求都被处理,避免内存泄漏
    httpMock.verify();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should add X-CSRF-TOKEN header to POST requests after fetching token', fakeAsync(() => {
    // 1. 定义模拟数据
    const mockTokenResponse = { XCSRFTOKEN: 'test-csrf-token' };
    const mockFormResponse: optout = {
      output: {
        somevar1: "somvalue",
        somevar2: false
      }
    };
    const mockPostData: optout = {
      output: {
        somevar1: "somvalue2",
        somevar2: false
      }
    };

    // 2. 调用getResponse触发Token获取
    optoutService.getResponse().subscribe(res => {
      expect(res).toEqual(mockFormResponse);
    });

    // 3. 模拟第一个获取Token的请求响应
    const tokenReq = httpMock.expectOne(GET_TOKEN_API);
    tokenReq.flush(mockTokenResponse);

    // 4. 模拟第二个获取表单的请求响应
    const formReq = httpMock.expectOne(GET_FORM_API);
    formReq.flush(mockFormResponse);

    // 等待异步流完成,确保拦截器已经存储了Token
    tick();

    // 5. 调用postResponse发起POST请求
    optoutService.postResponse(mockPostData).subscribe(res => {
      expect(res).toEqual(mockPostData);
    });

    // 6. 捕获POST请求并验证请求头
    const postReq = httpMock.expectOne(POST_API);
    expect(postReq.request.method).toBe('POST');
    // 断言POST请求头包含正确的X-CSRF-TOKEN
    expect(postReq.request.headers.get('X-CSRF-TOKEN')).toBe('test-csrf-token');

    // 模拟POST请求的响应
    postReq.flush(mockPostData);

    // 等待所有异步操作完成
    tick();
  }));
});

额外说明

  • afterEach里的httpMock.verify()用于确保所有发起的HTTP请求都被模拟响应,避免测试用例遗漏请求导致的问题。
  • 用fakeAsync和tick()可以同步处理异步操作,确保拦截器有足够时间处理响应并存储Token,之后再验证POST请求的头。
  • 明确每个请求的模拟顺序,严格对应getResponse()里的链式调用流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 19:02:31