Angular测试中HttpClient的POST请求在Jest中无法触发
Angular 12+/13中HttpInterceptor拦截POST请求时测试失败的问题
问题背景
实现了一个基础的HttpInterceptor,用于为POST请求添加Content-Type头信息,但在Angular 12、13版本(Node 14.17)的测试中,GET请求可正常触发,POST请求始终无法执行,第一个测试用例失败,报错Expected one matching request for criteria "Match URL: /data", found none.,且拦截器未打印请求日志。该功能在Angular 11中可正常工作。
拦截器代码
import { Injectable } from '@angular/core'; import { HttpHandler, HttpInterceptor, HttpRequest, } from '@angular/common/http'; @Injectable() export class MyInterceptor implements HttpInterceptor { public intercept( request: HttpRequest<Record<string, unknown>>, next: HttpHandler, ) { console.log('REQUEST WAS INTERCEPTED'); if (request.method === 'POST') { request = request.clone({ setHeaders: { 'Content-Type': 'application/json;charset=UTF-8', }, }); } return next.handle(request); } }
测试代码
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { Data } from '@angular/router'; import { HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http'; import { MyInterceptor } from './my-interceptor.service'; const testData: Data[] = [{ name: 'bob' }, { name: 'alice' }]; describe('PROVIDE_CONTENT_TYPE_HTTP_INTERCEPTOR', () => { let http: HttpClient; let httpTestingController: HttpTestingController; beforeEach(async () => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [{provide: HTTP_INTERCEPTORS, useClass: MyInterceptor, multi: true,}] }); http = TestBed.inject(HttpClient); httpTestingController = TestBed.inject(HttpTestingController); }); afterEach(() => { httpTestingController.verify(); }); it('handles POST requests', (done) => { http.post('/data', {}).subscribe((data: any) => { console.log('POST REQUEST HAS FIRED - NEVER HAPPENS'); expect(data).toEqual(testData); done(); }); const request = httpTestingController.expectOne('/data'); request.flush(testData); }); it('ignores GET requests', (done) => { http.get('/data').subscribe((data: any) => { console.log('GET REQUEST HAS FIRED - SUCCESS'); expect(data).toEqual(testData); done(); }); const request = httpTestingController.expectOne('/data'); request.flush(testData); }); });
问题原因与解决方案
原因
Angular 12+对HTTP拦截器的类型检查逻辑做了变更:当拦截器中声明的HttpRequest泛型(此处为Record<string, unknown>)与实际请求的泛型不匹配时,拦截器会被跳过,导致请求无法流转到测试控制器。而GET请求通常没有请求体,泛型不匹配的问题不会触发,因此GET请求可正常执行。
解决方案
将拦截器中的HttpRequest泛型改为unknown或any,消除类型不匹配的问题:
修改后的拦截器核心代码:
public intercept( request: HttpRequest<unknown>, // 替换原有的Record<string, unknown> next: HttpHandler, ) { console.log('REQUEST WAS INTERCEPTED'); if (request.method === 'POST') { request = request.clone({ setHeaders: { 'Content-Type': 'application/json;charset=UTF-8', }, }); } return next.handle(request); }
修改后,POST请求会被正常拦截,测试用例可顺利通过。
内容的提问来源于stack exchange,提问作者Maria K.
相关产品推荐
相关产品推荐

