无需TestBed用Jest测试Angular拦截器:验证Bearer Token及订阅无响应
问题分析与解决方案
拦截器代码问题
- HttpRequest不可变特性误用:
updatedRequest.headers.append('Authorization', ...)完全无效,因为HttpRequest和它的HttpHeaders都是不可变对象,append方法会返回新的HttpHeaders实例,不会修改原对象。而且你已经在clone时通过setHeaders设置了Authorization头,这行属于冗余错误代码,直接删除即可。 - switchMap分支未覆盖所有情况:当
data !== null不成立时,switchMap没有返回任何Observable,会导致流中断抛出错误,需要补充返回逻辑(比如return EMPTY或throwError)。
修复后的拦截器代码:
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { if (this.authService.isLoggedIn()) { return this.authService.acquireToken().pipe( switchMap((data: any) => { if (data !== null) { const authToken = data.accessToken; const updatedRequest = request.clone({ setHeaders: { Authorization: `Bearer ${authToken}`, }, }); return next.handle(updatedRequest); } // 补充data为null时的返回逻辑 return EMPTY; }) ); } else { return EMPTY; } }
单元测试代码问题
- 未Mock AuthService依赖:拦截器依赖
authService的isLoggedIn()和acquireToken()方法,你没有模拟这两个方法的返回值,导致拦截器要么进入else分支返回EMPTY,要么acquireToken()没有发出值,订阅自然无响应。 - Next.handle的Observable无Next值:你创建的Observable只调用了
subscriber.complete(),没有调用subscriber.next(),所以订阅的next回调不会执行,只能捕获complete事件。 - 验证对象错误:你打印的是原
requestMock的headers,而拦截器修改的是clone后的新请求对象,原对象不会被修改,必须在next.handle中捕获传入的新请求来验证头信息。
修复后的单元测试代码:
it('[Intercept] Should intercept the API requests and append auth token if user logged in', (done: DoneCallback) => { // 1. Mock AuthService const mockAuthService = { isLoggedIn: jest.fn().mockReturnValue(true), acquireToken: jest.fn().mockReturnValue(of({ accessToken: 'test-token-123' })) }; // 手动实例化拦截器,传入mock的authService const interceptor = new YourAuthInterceptor(mockAuthService as any); // 2. Mock HttpHandler,捕获传入的请求并验证头信息 const mockNext: Partial<HttpHandler> = { handle: jest.fn((req: HttpRequest<any>) => { // 验证Authorization头是否正确 expect(req.headers.get('Authorization')).toBe('Bearer test-token-123'); // 返回带有next值的Observable,确保订阅能触发next回调 return of({ type: 4 } as HttpEvent<any>); }) }; const requestMock = new HttpRequest('GET', '/test'); interceptor.intercept(requestMock, mockNext as HttpHandler).subscribe({ next: (event) => { console.log('API call intercepted', event); }, complete: () => { // 验证handle方法是否被调用 expect(mockNext.handle).toHaveBeenCalled(); done(); }, error: (err) => { done(err); } }); });
关键注意点
- 不使用TestBed时,必须手动实例化拦截器并传入所有依赖的mock对象,确保依赖方法返回预期值。
- 利用Jest的
jest.fn()来mock方法,方便验证调用情况和返回值。 - 验证请求头时,必须检查
next.handle接收的新请求对象,原请求对象是不可变的不会被修改。 - 订阅时要处理
next、complete、error三个回调,确保测试能正确结束(通过done()),避免超时。
内容的提问来源于stack exchange,提问作者Lewis Morgans
相关产品推荐
相关产品推荐

