Angular单元测试时无法为Mat Table的dataSource赋值测试数据
Angular Material 表格单元测试数据不生效解决方案
你这个问题是写Angular Material表格单元测试的常见坑,按下面步骤排查修改就能跑通:
- 第一优先级处理ngOnInit里的HTTP请求覆盖测试数据的问题:你现在手动给
defaultData赋值再调用filterTable,但ngOnInit里触发的HTTP请求是异步的,等前面同步代码执行完,请求返回会直接把sortedData覆盖成接口返回值,测试环境里接口要么返回空要么报错,自然渲染不出你要的3行数据。 - 第二要确认测试模块导入了完整的Material依赖:如果
TestBed配置里没导入MatTableModule、MatSortModule,Angular根本识别不了mat-table、mat-row这些指令,查出来的节点列表永远是空的。 - 第三要处理渲染时序问题:MatTable的行渲染不是同步执行的,靠
whenStable+done回调很容易漏等异步队列,用fakeAsync工具控制时序比手动写done回调稳得多。
可直接复用的修改示例
首先改测试模块的配置,先把依赖补全、mock掉数据接口:
// 顶部记得引入对应的模块和工具 import { MatTableModule } from '@angular/material/table'; import { MatSortModule } from '@angular/material/sort'; import { fakeAsync, flush, tick } from '@angular/core/testing'; import { of } from 'rxjs'; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [MatTableModule, MatSortModule], declarations: [YourBusinessComponent], providers: [ // mock掉组件里用来发HTTP请求的业务服务,避免真实请求干扰测试 { provide: YourDataService, useValue: { getCustomerList: jasmine.createSpy().and.returnValue(of([])) } } ] }).compileComponents(); });
然后改测试用例,不要手动硬塞数据,直接让mock接口返回你的测试数据,模拟真实运行逻辑:
const testCustomers: Customer[] = [ // 放3条你的测试实体即可 {id: 1, name: '客户A'}, {id: 2, name: '客户B'}, {id: 3, name: '客户C'}, ]; it("should correctly render 3 rows of customer data", fakeAsync(() => { // 让mock服务直接返回测试数据 const mockService = TestBed.inject(YourDataService); mockService.getCustomerList.and.returnValue(of(testCustomers)); // 第一次触发变更检测,执行ngOnInit发起请求、赋值数据 fixture.detectChanges(); // 清空所有异步队列,等请求返回、filterTable执行、数据赋值全跑完 flush(); // 先验证数据本身赋值正确,避免白等渲染 expect(component.sortedData.length).withContext('sortedData should have 3 items').toBe(3); // 触发第二次变更检测,通知表格渲染行 fixture.detectChanges(); // 等表格内部的异步渲染逻辑执行完 tick(); const tableRows = fixture.nativeElement.querySelectorAll("mat-row"); expect(tableRows.length).withContext('table should render 3 rows').toEqual(3); }));
其他常见踩坑点
如果你不想走mock接口的流程,非要手动赋值测试数据,一定要在第一次调用
fixture.detectChanges()(也就是触发ngOnInit)之前,先把组件的取数方法重写为空,再提前赋值sortedData,避免被ngOnInit的逻辑覆盖:it("should render rows with manual data", fakeAsync(() => { // 屏蔽ngOnInit里的取数逻辑 component.fetchCustomerData = () => {}; // 提前赋值 component.sortedData = testCustomers; fixture.detectChanges(); flush(); fixture.detectChanges(); tick(); expect(fixture.nativeElement.querySelectorAll("mat-row").length).toBe(3); }));
- 如果你是用
MatTableDataSource实例作为数据源,不要直接整个替换dataSource对象,要给dataSource.data属性赋值,不然表格订阅不到数据变更,永远不会更新渲染。 - 如果查不到
mat-row,先打印fixture.nativeElement.querySelector('mat-table')看表格节点本身有没有渲染出来,如果表格节点都不存在,要么是模块没导入,要么是模板里的*ngIf控制条件没满足。
内容的提问来源于stack exchange,提问作者Gary Kelly
相关产品推荐
相关产品推荐

