Angular中嵌套API调用的单元测试全覆盖实现方法咨询
问题描述
我正在尝试为Angular中的嵌套API调用编写单元测试,但不知道如何覆盖所有代码行。附上嵌套API调用的业务代码及已尝试的测试代码,当前测试可运行但无法贡献代码覆盖率,寻求能覆盖所有代码行的测试方案,支持Jest/Jasmine框架。
业务代码
this.openService .createDocument(PIReq, this.id) .subscribe({ next: (res1) => { const applicationReq = AppUtil.getApplicationRequest( this.openService.personDetails, res1.id ); this.openService .createPersonAppliation(applicationReq, this.id) .subscribe({ next: (res2) => { const personAppReqInfo: PersonApplicationIds = { documentId: res1.id, personApplicationId: res2.id, }; if (this.openDataStoreService.details.length > 0) { this.submitApplicationRequest(); this.invokeRequest(); } else { this.invokeRequest(); } }, error: () => { this.showSpinner = false; }, }); }, error: () => { this.showSpinner = false; }, });
已尝试的测试代码
let sampleApplicationResponse!: ApplicationResponse; jest.spyOn(openService, 'createPersonAppliation').mockReturnValueOnce(of(sampleApplicationResponse)); openService.createPersonAppliation(applicationReq, component.id) .subscribe({ next: applicationRes => { expect(applicationRes).toBe(sampleApplicationResponse); } });
全覆盖测试方案
要覆盖所有代码路径,需要测试4种核心场景:双API调用成功(分details有数据/无数据)、createDocument调用失败、createPersonAppliation调用失败。以下是基于Jest的完整实现:
测试前置准备
先在测试文件中完成组件和依赖的初始化:
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { of, throwError } from 'rxjs'; import { YourComponent } from './your.component'; import { OpenService } from './open.service'; import { OpenDataStoreService } from './open-data-store.service'; import { AppUtil } from './app.util'; describe('YourComponent', () => { let component: YourComponent; let fixture: ComponentFixture<YourComponent>; let openService: jest.Mocked<OpenService>; let openDataStoreService: jest.Mocked<OpenDataStoreService>; beforeEach(async () => { // 创建依赖的Mock实例 const openServiceMock = { createDocument: jest.fn(), createPersonAppliation: jest.fn(), personDetails: {} // 根据实际结构模拟 } as jest.Mocked<OpenService>; const dataStoreMock = { details: [] } as jest.Mocked<OpenDataStoreService>; await TestBed.configureTestingModule({ declarations: [YourComponent], providers: [ { provide: OpenService, useValue: openServiceMock }, { provide: OpenDataStoreService, useValue: dataStoreMock } ] }).compileComponents(); fixture = TestBed.createComponent(YourComponent); component = fixture.componentInstance; openService = TestBed.inject(OpenService) as jest.Mocked<OpenService>; openDataStoreService = TestBed.inject(OpenDataStoreService) as jest.Mocked<OpenDataStoreService>; // 静态工具方法Mock jest.spyOn(AppUtil, 'getApplicationRequest').mockReturnValue({ mock: 'request' }); // 组件内部方法Mock,用于断言调用 component.submitApplicationRequest = jest.fn(); component.invokeRequest = jest.fn(); });
场景1:双API成功 + details有数据
it('should call both submitApplicationRequest and invokeRequest when APIs succeed with non-empty details', () => { // 模拟API返回值 const mockDocRes = { id: 'doc_001' }; const mockAppRes = { id: 'app_001' }; openService.createDocument.mockReturnValue(of(mockDocRes)); openService.createPersonAppliation.mockReturnValue(of(mockAppRes)); // 设置details有数据 openDataStoreService.details = [{ id: 'detail_001' }]; // 模拟组件参数并触发API调用(假设触发方法为submitAction) component.id = 'comp_001'; const mockPIReq = { name: 'test' }; component.submitAction(mockPIReq); // 断言API调用参数正确性 expect(openService.createDocument).toHaveBeenCalledWith(mockPIReq, 'comp_001'); expect(AppUtil.getApplicationRequest).toHaveBeenCalledWith(openService.personDetails, 'doc_001'); expect(openService.createPersonAppliation).toHaveBeenCalledWith({ mock: 'request' }, 'comp_001'); // 断言后续业务方法调用 expect(component.submitApplicationRequest).toHaveBeenCalled(); expect(component.invokeRequest).toHaveBeenCalled(); });
场景2:双API成功 + details无数据
it('should only call invokeRequest when APIs succeed with empty details', () => { const mockDocRes = { id: 'doc_001' }; const mockAppRes = { id: 'app_001' }; openService.createDocument.mockReturnValue(of(mockDocRes)); openService.createPersonAppliation.mockReturnValue(of(mockAppRes)); // 保持details为空数组 openDataStoreService.details = []; component.id = 'comp_001'; const mockPIReq = { name: 'test' }; component.submitAction(mockPIReq); expect(component.submitApplicationRequest).not.toHaveBeenCalled(); expect(component.invokeRequest).toHaveBeenCalled(); });
场景3:createDocument调用失败
it('should set showSpinner to false when createDocument fails', () => { component.showSpinner = true; openService.createDocument.mockReturnValue(throwError(() => new Error('API Error'))); const mockPIReq = { name: 'test' }; component.submitAction(mockPIReq); expect(component.showSpinner).toBe(false); });
场景4:createPersonAppliation调用失败
it('should set showSpinner to false when createPersonAppliation fails', () => { component.showSpinner = true; const mockDocRes = { id: 'doc_001' }; openService.createDocument.mockReturnValue(of(mockDocRes)); openService.createPersonAppliation.mockReturnValue(throwError(() => new Error('API Error'))); const mockPIReq = { name: 'test' }; component.submitAction(mockPIReq); expect(component.showSpinner).toBe(false); });
});
内容的提问来源于stack exchange,提问作者Tamilvanan
相关产品推荐
相关产品推荐

