如何为Angular Signal Queries添加单元测试?
如何Mock Angular Signal Queries(viewChild/viewChildren等)进行单元测试
问题背景
使用Angular的Signal版查询API(viewChild/viewChildren/contentChild/contentChildren)时,直接重写组件的查询信号属性来Mock值的方式不仅不够优雅,还无法保持属性的readonly特性。
更优解决方案
方案1:利用测试模板让查询自然获取Mock元素
如果组件模板中原本就包含目标元素,或可以通过TestBed临时修改模板注入测试元素,让Angular的Signal查询自动获取到Mock元素:
it('should scroll to the top', () => { // 创建组件实例 const fixture = TestBed.createComponent(MyComponent); const component = fixture.componentInstance; // 若组件模板未包含#scrollContainer,可临时修改模板 TestBed.overrideComponent(MyComponent, { set: { template: '<div #scrollContainer></div>' } }); fixture.detectChanges(); // 触发变更检测,让viewChild完成查询 // 获取目标元素并监听scroll方法 const scrollElement = fixture.debugElement.query(By.css('[#scrollContainer]')).nativeElement; const scrollSpy = spyOn(scrollElement, 'scroll'); // 调用组件方法 component.scrollToTop(); // 验证调用 expect(scrollSpy).toHaveBeenCalledOnceWith({ top: 0 }); });
方案2:Spy拦截只读查询信号
如果不想修改模板,可通过Spy拦截组件的只读查询信号属性,返回模拟的Signal对象,保持属性的readonly特性:
it('should scroll to the top', () => { const component = TestBed.inject(MyComponent); // 创建Mock元素及Spy const mockDiv = document.createElement('div'); const scrollSpy = spyOn(mockDiv, 'scroll'); // 模拟只读Signal的调用行为 spyOnProperty(component, 'myScrollContainer').and.returnValue( () => ({ nativeElement: mockDiv }) // 返回可调用的模拟Signal ); // 执行测试方法 component.scrollToTop(); // 验证结果 expect(scrollSpy).toHaveBeenCalledOnceWith({ top: 0 }); });
为什么不推荐直接重写Signal属性
直接赋值重写readonly的查询信号会违反TypeScript的只读约束,同时绕过了Angular的查询生命周期,测试逻辑与实际运行逻辑脱节,降低了测试的可信度。
内容的提问来源于stack exchange,提问作者JSON Derulo
相关产品推荐
相关产品推荐

