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

求助:使用Jest为Angular HttpInterceptor编写单元测试

Angular GlobalErrorIntercepterService 单元测试实现

待测试拦截器代码

import {
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { catchError, Observable, throwError } from 'rxjs';
import { UnauthorizedErrorDialogComponent } from 'src/app/shared/components/unauthorized-error-dialog/unauthorized-error-dialog.component';
import { SvDialogService } from 'sv-shared-library';

@Injectable({
  providedIn: 'root'
})
export class GlobalErrorIntercepterService
  implements HttpInterceptor
{
  constructor(private dialog: SvDialogService) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError((err) => {
        console.log(err);
        if (err.status === 401) {
          this.dialog.open(UnauthorizedErrorDialogComponent);
        }
        return throwError(() => new Error(err));
      })
    );
  }
}

单元测试实现

基于Angular测试框架结合Jasmine编写测试,核心是Mock依赖服务并验证拦截器行为是否符合预期。

测试依赖与初始化配置

先导入必要模块并完成TestBed配置,Mock弹窗服务与HTTP处理器:

import { TestBed } from '@angular/core/testing';
import { GlobalErrorIntercepterService } from './global-error-intercepter.service';
import { SvDialogService } from 'sv-shared-library';
import { UnauthorizedErrorDialogComponent } from 'src/app/shared/components/unauthorized-error-dialog/unauthorized-error-dialog.component';
import { HttpRequest, HttpHandler, HttpErrorResponse } from '@angular/common/http';
import { throwError } from 'rxjs';

describe('GlobalErrorIntercepterService', () => {
  let interceptor: GlobalErrorIntercepterService;
  let dialogService: jasmine.SpyObj<SvDialogService>;
  let httpHandler: jasmine.SpyObj<HttpHandler>;

  beforeEach(() => {
    const dialogSpy = jasmine.createSpyObj('SvDialogService', ['open']);
    const handlerSpy = jasmine.createSpyObj('HttpHandler', ['handle']);

    TestBed.configureTestingModule({
      providers: [
        GlobalErrorIntercepterService,
        { provide: SvDialogService, useValue: dialogSpy },
        { provide: HttpHandler, useValue: handlerSpy }
      ]
    });

    interceptor = TestBed.inject(GlobalErrorIntercepterService);
    dialogService = TestBed.inject(SvDialogService) as jasmine.SpyObj<SvDialogService>;
    httpHandler = TestBed.inject(HttpHandler) as jasmine.SpyObj<HttpHandler>;
  });

测试用例1:错误状态码为401时,打开指定弹窗

it('should open UnauthorizedErrorDialogComponent when error status is 401', (done) => {
    const mockReq = new HttpRequest('GET', '/test');
    const mockError = new HttpErrorResponse({
      status: 401,
      error: 'Unauthorized'
    });

    httpHandler.handle.and.returnValue(throwError(() => mockError));

    interceptor.intercept(mockReq, httpHandler).subscribe({
      error: () => {
        expect(dialogService.open).toHaveBeenCalledWith(UnauthorizedErrorDialogComponent);
        done();
      }
    });
  });

测试用例2:错误状态码非401时,抛出错误且不触发弹窗

it('should throw error and not trigger dialog when error status is not 401', (done) => {
    const mockReq = new HttpRequest('GET', '/test');
    const mockError = new HttpErrorResponse({
      status: 404,
      error: 'Not Found'
    });

    httpHandler.handle.and.returnValue(throwError(() => mockError));

    interceptor.intercept(mockReq, httpHandler).subscribe({
      error: (err) => {
        expect(err).toBeInstanceOf(Error);
        expect(err.message).toBe(mockError);
        expect(dialogService.open).not.toHaveBeenCalled();
        done();
      }
    });
  });
});

关键说明

  • 用jasmine.createSpyObj创建Mock对象,方便验证方法调用情况
  • 通过HttpErrorResponse模拟不同状态码的HTTP错误
  • 订阅拦截器返回的Observable,在error回调中完成断言校验
  • 使用done回调处理异步测试逻辑,确保断言在异步操作完成后执行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 00:20:39