You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 13:17:01