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

使用Jest和Spectator测试Angular响应拦截器

如何在Angular 10 + Jest + Spectator环境中测试HTTP拦截器的HttpHandler参数?

我正在尝试测试一个用于修改HTTP请求响应的Interceptor,以下是我的现有代码:

拦截器代码

@Injectable()
export class ResponseCamelCaseInterceptor implements HttpInterceptor {
  intercept(
    httpRequest: HttpRequest<Record<string, unknown>>,
    httpHandler: HttpHandler,
  ): Observable<HttpEvent<Record<string, unknown>>> {
    return httpHandler.handle(httpRequest).pipe(
      filter(
        (value): value is HttpResponse<Record<string, unknown>> => value instanceof HttpResponse,
      ),
      filter(({ body }) => isPlainObject(body)),
      map(httpEvent => httpEvent.clone({ body: snakeToCamelCase(httpEvent.body) }),
      ),
    );
  }
}

测试文件代码(待完善)

describe(ResponseCamelCaseInterceptor.name, () => {
  const createService = createServiceFactory(ResponseCamelCaseInterceptor);
  test('some description', () => {
    const { service } = createService();
    const fakeHttpRequest = new HttpRequest('POST', 'https://...', { custom_key: '1' });
    service.intercept(fakeHttpRequest, 'what should I put here for HttpHandler?').subscribe(() => {
      // expect(httpResponse.body).toStrictEqual({ customKey: '1' });
    });
  });
});

我使用的版本为Angular 10.x.y、Jest 26.x.y和Spectator 5.x.y,请问测试中HttpHandler参数该如何设置?


解决方案:模拟HttpHandler并返回预设响应

在测试拦截器时,你需要模拟HttpHandler的handle方法,让它返回一个包含预设响应的Observable,这样拦截器就能处理这个响应,你也可以验证处理后的结果是否符合预期。结合你用的工具栈,有两种简洁的实现方式:

方法1:利用Spectator的依赖模拟功能

Spectator的createServiceFactory支持传入依赖的模拟对象,你可以用mockProvider快速创建HttpHandler的模拟实例:

import { HttpHandler, HttpResponse } from '@angular/common/http';
import { mockProvider } from '@ngneat/spectator/jest';
import { of } from 'rxjs';

describe(ResponseCamelCaseInterceptor.name, () => {
  const createService = createServiceFactory({
    service: ResponseCamelCaseInterceptor,
    providers: [
      mockProvider(HttpHandler, {
        handle: jest.fn((req) => of(new HttpResponse({ body: { custom_key: '1' } })))
      })
    ]
  });

  test('should convert response body from snake_case to camelCase', async () => {
    const { service, inject } = createService();
    const httpHandler = inject(HttpHandler);
    const fakeHttpRequest = new HttpRequest('POST', 'https://...', { custom_key: '1' });

    const response = await service.intercept(fakeHttpRequest, httpHandler)
      .pipe(first())
      .toPromise();

    // 验证响应体的驼峰转换是否生效
    expect(response.body).toStrictEqual({ customKey: '1' });
    // 验证HttpHandler的handle方法是否被正确调用
    expect(httpHandler.handle).toHaveBeenCalledWith(fakeHttpRequest);
  });
});

方法2:手动创建模拟的HttpHandler实例

如果不想依赖Spectator的注入功能,也可以直接用Jest的mock函数手动构建HttpHandler:

import { HttpHandler, HttpResponse } from '@angular/common/http';
import { of } from 'rxjs';
import { first } from 'rxjs/operators';

describe(ResponseCamelCaseInterceptor.name, () => {
  const createService = createServiceFactory(ResponseCamelCaseInterceptor);

  test('should convert response body from snake_case to camelCase', async () => {
    const { service } = createService();
    const fakeHttpRequest = new HttpRequest('POST', 'https://...', { custom_key: '1' });
    
    // 构建模拟的HttpHandler,预设返回的响应体
    const mockHttpHandler: Partial<HttpHandler> = {
      handle: jest.fn(() => of(new HttpResponse({ body: { custom_key: '1' } })))
    };

    const response = await service.intercept(fakeHttpRequest, mockHttpHandler as HttpHandler)
      .pipe(first())
      .toPromise();

    expect(response.body).toStrictEqual({ customKey: '1' });
    expect(mockHttpHandler.handle).toHaveBeenCalledWith(fakeHttpRequest);
  });
});

关键细节说明

  • 用of(new HttpResponse(...))创建包含预设响应的Observable,让拦截器的pipe链能正常处理数据;
  • 用Jest的jest.fn()模拟handle方法,方便验证方法的调用情况和参数;
  • 由于拦截器返回的是Observable,测试属于异步操作,这里用async/await配合first()操作符来确保Jest等待异步流程完成,避免测试提前结束。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 23:17:30