Angular中PassengerDetailsService的handleError单元测试覆盖率优化求助
提升PassengerDetailsService中handleError方法的代码覆盖率
问题背景
需要为passenger-detail.service.specs.ts中的handleError方法提升代码覆盖率,当前收到「function not covered」和「statement not covered」的未覆盖提示,服务实现代码如下:
import { Injectable } from "@angular/core"; import { Observable, catchError, throwError } from "rxjs"; import { HttpClient, HttpErrorResponse, HttpHeaders, } from "@angular/common/http"; import { AuthCookiesService } from "@britishairways-ent/cookie"; import { environment } from "src/environments/environment"; import { PassengerApis } from "src/app/interfaces/apis-passenger.interface"; @Injectable({ providedIn: "root", }) export class PassengerDetailsService { constructor( private http: HttpClient, private authCookiesService: AuthCookiesService ) {} addApisData(passengerDetails: PassengerApis): Observable<any> { const url = environment.SAVE_APIS; return this.http .post(url, passengerDetails, { observe: "response", headers: new HttpHeaders({ "Content-Type": "application/json", Authorization: "Bearer " + this.authCookiesService.getPublicAccessToken(), }), }) .pipe(catchError(this.handleError)); } handleError(error: HttpErrorResponse) { if ( error.error && typeof error.error.code !== "undefined" && typeof error.error.description !== "undefined" ) { return throwError(() => error.error.code); } } }
解决方案
1. 先完善handleError方法
原方法在不符合if条件时无返回值,会导致运行时错误,同时也无法覆盖该分支,先补充默认返回逻辑:
handleError(error: HttpErrorResponse) { if ( error.error && typeof error.error.code !== "undefined" && typeof error.error.description !== "undefined" ) { return throwError(() => error.error.code); } // 处理不符合条件的错误场景,返回默认错误信息 return throwError(() => error.message || 'Unknown error'); }
2. 编写测试用例覆盖所有分支
在passenger-detail.service.specs.ts中添加以下测试代码,覆盖handleError的所有执行路径:
import { TestBed } from '@angular/core/testing'; import { PassengerDetailsService } from './passenger-details.service'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { AuthCookiesService } from '@britishairways-ent/cookie'; import { throwError } from 'rxjs'; describe('PassengerDetailsService', () => { let service: PassengerDetailsService; let httpClientSpy: jasmine.SpyObj<HttpClient>; let authCookiesSpy: jasmine.SpyObj<AuthCookiesService>; beforeEach(() => { // 创建依赖的模拟对象 const httpSpy = jasmine.createSpyObj('HttpClient', ['post']); const authCookieSpy = jasmine.createSpyObj('AuthCookiesService', ['getPublicAccessToken']); TestBed.configureTestingModule({ providers: [ PassengerDetailsService, { provide: HttpClient, useValue: httpSpy }, { provide: AuthCookiesService, useValue: authCookieSpy } ] }); service = TestBed.inject(PassengerDetailsService); httpClientSpy = TestBed.inject(HttpClient) as jasmine.SpyObj<HttpClient>; authCookiesSpy = TestBed.inject(AuthCookiesService) as jasmine.SpyObj<AuthCookiesService>; // 模拟token返回值 authCookiesSpy.getPublicAccessToken.and.returnValue('mock-token'); }); describe('handleError', () => { // 测试符合条件的错误场景 it('返回错误码当错误对象包含code和description', (done: DoneFn) => { const testError = new HttpErrorResponse({ error: { code: 'API_ERR_001', description: '无效乘客信息' }, status: 400, statusText: 'Bad Request' }); service.handleError(testError).subscribe({ error: (err) => { expect(err).toBe('API_ERR_001'); done(); } }); }); // 测试所有不符合条件的错误分支 it('返回默认错误信息当错误对象缺少code或description', (done: DoneFn) => { // 场景1:error.error不存在 const errorWithoutErrorObj = new HttpErrorResponse({ status: 500, statusText: 'Internal Server Error' }); service.handleError(errorWithoutErrorObj).subscribe({ error: (err) => { expect(err).toBe('Internal Server Error'); } }); // 场景2:error.error存在但无code const errorWithoutCode = new HttpErrorResponse({ error: { description: '参数错误' }, status: 400, statusText: 'Bad Request' }); service.handleError(errorWithoutCode).subscribe({ error: (err) => { expect(err).toBe('Bad Request'); } }); // 场景3:error.error存在但无description const errorWithoutDesc = new HttpErrorResponse({ error: { code: 'API_ERR_002' }, status: 400, statusText: 'Bad Request' }); service.handleError(errorWithoutDesc).subscribe({ error: (err) => { expect(err).toBe('Bad Request'); done(); } }); }); // 测试通过addApisData触发handleError的场景 it('当addApisData请求失败时触发handleError', (done: DoneFn) => { const testError = new HttpErrorResponse({ error: { code: 'API_ERR_003', description: '保存失败' }, status: 400, statusText: 'Bad Request' }); // 模拟post请求抛出错误 httpClientSpy.post.and.returnValue(throwError(() => testError)); service.addApisData({} as any).subscribe({ error: (err) => { expect(err).toBe('API_ERR_003'); expect(httpClientSpy.post).toHaveBeenCalled(); done(); } }); }); }); });
3. 覆盖率说明
- 第一个测试覆盖
handleError中if条件为真的分支。 - 第二个测试覆盖if条件为假的所有子分支(无error对象、缺code、缺description)。
- 第三个测试覆盖
handleError通过addApisData被调用的路径,确保方法的调用链路也被覆盖。
内容的提问来源于stack exchange,提问作者Ash
相关产品推荐
相关产品推荐

