如何为Angular的@ViewChildren QueryList创建模拟DOM编写单元测试
Angular单元测试:模拟QueryList与DOM元素宽度
问题说明
需要为使用@ViewChildren获取的QueryList编写单元测试,测试逻辑为遍历每个ElementRef获取元素宽度,判断是否能容纳在容器div内,需实现QueryList引用的DOM模拟。
组件模板(Test.component.html)
<div #containerRef> <span *ngFor="let val of data| slice: 0:linksShown;" #listEle> <a href="{{ val.link }}">{{ val.name }}</a> </span> </div>
组件代码(Test.component.ts)
import { ElementRef, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.component.scss'], }) export class TestComponent implements OnInit { @Input() data; @ViewChild('containerRef', { read: ElementRef, static: true }) containerRef: ElementRef; listRef: QueryList<ElementRef>; @ViewChildren('listEle') set listEle(ref: QueryList<ElementRef>) { this.listRef = ref; } linksShown: number; constructor() {} ngOnInit() { let count = 0; this.listRef.toArray().forEach((ele) => { count += ele.nativeElement.clientWidth; if (count < this.containerRef.nativeElement.clientWidth) { count++; } }); this.linksShown = count; } }
现有测试代码(Test.component.spec.ts)
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TestComponent } from './test.component'; describe('TestComponent', () => { let component: TestComponent; let fixture: ComponentFixture<TestComponent>; const data = [ { name: 'test1', link: '/test1' }, { name: 'test2', link: '/test2' } ] beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [TestComponent], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(TestComponent); component = fixture.componentInstance; component.data = data; fixture.detectChanges(); }); it('should calculate correct linksShown count', () => { component.ngOnInit(); expect(component.linksShown).toEqual(2); }); })
解决方案:模拟DOM与QueryList
要测试依赖DOM宽度的逻辑,无需真实渲染DOM,直接模拟所需属性即可:
1. 模拟容器元素宽度
直接给containerRef.nativeElement的clientWidth赋值,模拟容器的可用宽度:
component.containerRef.nativeElement.clientWidth = 200; // 自定义容器宽度
2. 模拟QueryList与列表元素
创建模拟的ElementRef数组,每个元素指定clientWidth,再模拟QueryList的toArray()方法返回该数组:
// 模拟每个列表项的宽度 const mockListElements = [ { nativeElement: { clientWidth: 80 } }, { nativeElement: { clientWidth: 90 } } ] as ElementRef[]; // 模拟QueryList对象 component.listRef = { toArray: () => mockListElements } as QueryList<ElementRef>;
3. 修正测试用例并验证
将上述模拟逻辑整合到测试用例中,同时修正原测试的错误(比如OnInit改为ngOnInit):
it('should calculate correct linksShown count', () => { // 模拟容器宽度 component.containerRef.nativeElement.clientWidth = 200; // 模拟列表元素与QueryList const mockListElements = [ { nativeElement: { clientWidth: 80 } }, { nativeElement: { clientWidth: 90 } } ] as ElementRef[]; component.listRef = { toArray: () => mockListElements } as QueryList<ElementRef>; // 触发初始化逻辑 component.ngOnInit(); // 根据组件逻辑计算:80+1 +90+1 = 172,这里断言对应结果 expect(component.linksShown).toBe(172); });
额外说明
- 若需测试不同宽度场景,只需调整
clientWidth的数值即可 - 原组件逻辑中
linksShown最终赋值为宽度累加后的结果,若你的实际需求是统计可容纳的元素数量,需先修正组件内的逻辑
内容的提问来源于stack exchange,提问作者Yashwanth
相关产品推荐
相关产品推荐

