Angular Jasmine单元测试嵌套ViewChild组件disable方法报错
问题原因
测试报错核心是两个写法错误:
spyOn仅能劫持对象的方法、或者get/set访问器属性,你定义的DashboardSliderStubComponent里的swiper是普通实例属性,spyOn(component.dashboardSlider, 'swiper').and.returnValue(...)这行代码完全没有生效,运行时取到的swiper始终是你初始化的空对象{} as SwiperComponent,空对象上没有swiperRef属性,读取swiperRef.disable自然触发undefined错误。- 你手动给
component.dashboardSlider赋值的时机错误:fixture.detectChanges()执行时Angular会自动完成@ViewChild视图查询,把模板实际渲染出来的stub组件实例赋值给dashboardSlider属性,你之前手动挂载了spy的实例会被直接覆盖。
正确测试写法
不需要用复杂的spy劫持属性,直接构造逐层的mock对象、在正确时机赋值即可,修正后代码如下:
// 简化stub组件,不需要提前初始化swiper属性 @Component({ selector: 'app-dashboard-slider', template: '', }) class DashboardSliderStubComponent { swiper?: SwiperComponent; } beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [DashboardPage, DashboardSliderStubComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA], }).compileComponents(); fixture = TestBed.createComponent(DashboardPage); component = fixture.componentInstance; // 先执行变更检测,让Angular完成@ViewChild查询,拿到真实渲染的子组件实例 fixture.detectChanges(); })); fdescribe('Test ionViewDidLeave', () => { it('should call swiper disable method when ionViewDidLeave trigger', () => { // 构造spy和逐层mock对象 const disableSpy = jasmine.createSpy('disable'); const mockSwiperInstance = { swiperRef: { disable: disableSpy } } as unknown as SwiperComponent; // 给查询到的真实子组件实例赋值mock component.dashboardSlider!.swiper = mockSwiperInstance; // 触发生命周期钩子 component.ionViewDidLeave(); // 验证结果 expect(disableSpy).toHaveBeenCalled(); }); });
补充说明
- 不要在
fixture.detectChanges()之前手动给@ViewChild装饰的属性赋值,Angular变更检测流程会覆盖手动赋值的内容,必须等首次detectChanges执行完成、视图查询结束后,再操作子组件实例的属性。 - 普通实例属性不要用
spyOn做mock,直接赋值是最简单可靠的方案。如果一定要用spyOn劫持swiper的取值,需要把stub组件里的swiper改成get访问器:
这种场景下class DashboardSliderStubComponent { get swiper(): SwiperComponent { return {} as SwiperComponent; } }spyOn才能正常劫持属性的返回值,但写法比直接赋值繁琐,非必要不使用。 - 你之前注释掉的
providers里提供DashboardSliderComponent的写法是无效的:@ViewChild查询的是模板渲染出来的组件实例,不是依赖注入容器里的服务实例,只需要在declarations里声明stub组件即可,不需要在providers里配置。
原报错信息:
TypeError: Cannot read properties of undefined (reading 'disable')
内容的提问来源于stack exchange,提问作者ngSamurai
相关产品推荐
相关产品推荐

