使用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
相关产品推荐
相关产品推荐

