Angular 11+Karma-Jasmine单元测试报错:Cannot read properties of undefined (reading 'filter')
Angular 11单元测试:TypeError: Cannot read properties of undefined (reading 'filter') 解决方法
问题场景
在使用Angular 11搭配Karma-Jasmine进行单元测试时,出现错误TypeError: Cannot read properties of undefined (reading 'filter'),排查后确认错误出现在组件ngOnInit调用的getserviceProvider函数中——对ServiceProvider执行filter方法时,ServiceProvider为undefined。尝试修复时曾导致所有测试用例失败。
相关代码片段
组件TS文件
ngOnInit(): void { this.getserviceProvider(); } getserviceProvider() { this.Servicefile.getUpgWalletServiceProviders().subscribe( (data) => { this.UpgWalletServiceProviders = data?.body; this.ServiceProvider = data?.body; this.selectedDisplayName = this.ServiceProvider.filter((t: any) => t.isActive === true)[0].displayName; }, (error) => { this.errorsService.handle(error); }, ); }
单元测试文件
it('should create', () => { expect(component).toBeTruthy(); }); it('getMaxDigitalWalletsCount called successfully', fakeAsync(() => { spyOn(MockBusinessSettingsService, 'getMaxDigitalWalletsCount').and.callThrough(); component.ngOnInit(); expect(MockBusinessSettingsService.getMaxDigitalWalletsCount).toHaveBeenCalled(); tick(100); expect(component.isLoadingResults).toBeFalse(); fixture.detectChanges(); expect(component.ChashInsForm.value).toEqual( jasmine.objectContaining({ maxDigitalWalletsCount: listData.digitalWalletsCountUPG.body.maxDigitalWalletsCount, }), ); }));
问题分析
- 组件代码健壮性不足:虽然用
data?.body赋值给ServiceProvider,但后续直接调用filter方法时未做判空处理。如果getUpgWalletServiceProviders返回的data.body为null/undefined,ServiceProvider就会是undefined,触发filter调用错误。 - 单元测试未Mock异步服务:测试中没有对
Servicefile.getUpgWalletServiceProviders进行Mock,导致组件实际调用该方法时没有得到预期的返回值,ServiceProvider无法被正确赋值,进而引发错误。
修复步骤
1. 增强组件代码的健壮性
修改getserviceProvider函数,在调用filter前先判空,同时避免直接访问数组索引(防止数组为空时的越界错误):
getserviceProvider() { this.Servicefile.getUpgWalletServiceProviders().subscribe( (data) => { this.UpgWalletServiceProviders = data?.body; this.ServiceProvider = data?.body; // 先判断ServiceProvider是否存在且不为空数组 if (this.ServiceProvider?.length) { const activeProvider = this.ServiceProvider.find(t => t.isActive === true); this.selectedDisplayName = activeProvider?.displayName || ''; } else { this.selectedDisplayName = ''; // 或者设置业务需求的默认值 } }, (error) => { this.errorsService.handle(error); }, ); }
2. 完善单元测试的Mock逻辑
在测试中MockServicefile.getUpgWalletServiceProviders方法,返回包含有效数据的Observable,确保组件能正确执行后续逻辑:
// 针对should create用例补充Mock逻辑 it('should create and initialize service providers', fakeAsync(() => { // 模拟服务返回的有效数据 const mockProviders = [ { isActive: true, displayName: 'Active Provider' }, { isActive: false, displayName: 'Inactive Provider' } ]; // Spy并返回模拟的Observable spyOn(MockServicefile, 'getUpgWalletServiceProviders').and.returnValue(of({ body: mockProviders })); component.ngOnInit(); tick(); // 等待异步操作完成 fixture.detectChanges(); expect(component.ServiceProvider).toEqual(mockProviders); expect(component.selectedDisplayName).toBe('Active Provider'); expect(component).toBeTruthy(); })); // 完善原有的getMaxDigitalWalletsCount测试用例 it('getMaxDigitalWalletsCount called successfully', fakeAsync(() => { // 必须Mock getUpgWalletServiceProviders,避免组件报错 spyOn(MockServicefile, 'getUpgWalletServiceProviders').and.returnValue(of({ body: [] })); spyOn(MockBusinessSettingsService, 'getMaxDigitalWalletsCount').and.callThrough(); component.ngOnInit(); tick(100); expect(MockBusinessSettingsService.getMaxDigitalWalletsCount).toHaveBeenCalled(); expect(component.isLoadingResults).toBeFalse(); fixture.detectChanges(); expect(component.ChashInsForm.value).toEqual( jasmine.objectContaining({ maxDigitalWalletsCount: listData.digitalWalletsCountUPG.body.maxDigitalWalletsCount, }), ); }));
关键注意点
- 所有涉及异步服务调用的测试用例,都需要Mock对应的服务方法,返回可预测的Observable,避免依赖真实服务的响应。
- 组件中对异步返回的数据处理必须添加判空逻辑,防止出现undefined/null调用方法的错误。
内容的提问来源于stack exchange,提问作者Nourhan Elsayed
相关产品推荐
相关产品推荐

