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

使用Jasmine单元测试时无法模拟keyup事件更新过滤值

问题:过滤值更新的Jasmine单元测试失败

我设置过滤字段初始值为空,触发keyup事件后期望它更新为过滤组件的当前值,但编写的Jasmine单元测试始终获取不到更新后的值,只能拿到初始值。

测试代码

describe('Component', () => {
    let component: Component1;
    let fixture: ComponentFixture<Component1>;
    let mockDialog: MatDialog;
    let mockDataService: Service1;
    let mockToastr: ToastrService;
    let mockPaginator: MatPaginator;
    let mockSort: MatSort;
    let mockFilterElement: ElementRef<HTMLInputElement>;

    beforeEach(waitForAsync(() => {
        TestBed.configureTestingModule({
            declarations: [Component1],
            imports: [MatPaginatorModule],
            providers: [
                { provide: MatDialog, useValue: jasmine.createSpyObj('MatDialog', ['open']) },
                { provide: Service1, useValue: jasmine.createSpyObj('Service1', ['fetchData', 'update', 'getAll']) },
                { provide: ToastrService, useValue: jasmine.createSpyObj('ToastrService', ['success', 'error']) },
            ],
        }).compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(Component1);
        component = fixture.componentInstance;

        mockDialog = TestBed.inject(MatDialog);
        mockDataService = TestBed.inject(Service1);
        mockToastr = TestBed.inject(ToastrService);

        mockPaginator = jasmine.createSpyObj('MatPaginator', ['previousPage', 'nextPage']);
        mockSort = jasmine.createSpyObj('MatSort', ['sort']);

        mockFilterElement = new ElementRef<HTMLInputElement>(document.createElement('input'));

        component.dialog = mockDialog;
        component.dataService = mockDataService;
        component.paginator = mockPaginator;
        component.sort = mockSort;
        component.filter = mockFilterElement;

        component.dataSource = new ExampleDataSource(null, mockPaginator, mockSort);

        fixture.detectChanges();
    });

    it('should handle filter changes', async () => {
        const filterValue = 'test';
        mockFilterElement.nativeElement.value = filterValue;

        await component.ngOnInit();
                
        const event = new Event('keyup');
        mockFilterElement.nativeElement.dispatchEvent(event);

        expect(component.dataSource.filter).toBe(filterValue);
    });
  // More Tests...
});

组件代码

@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild('filter', { static: true }) filter: ElementRef;

ngOnInit(): void {
    Observable.fromEvent(this.filter.nativeElement, 'keyup').pipe(
        debounceTime(150),
        distinctUntilChanged()
    ).subscribe(() => {
        if (!this.dataSource) { return; }
        this.dataSource.filter = this.filter.nativeElement.value
    });
}

问题原因

  1. 重复调用ngOnInit:fixture.detectChanges()执行时已经触发了组件的ngOnInit,手动调用await component.ngOnInit()会重复绑定keyup事件,且ngOnInit是同步方法,await无实际作用。
  2. 防抖未等待:组件里用了debounceTime(150),单元测试触发keyup事件后直接断言,此时防抖还未完成,订阅回调尚未执行,因此dataSource.filter还是初始值。

修复方案

使用Jasmine的fakeAsync和tick()模拟时间流逝,等待防抖完成,同时移除不必要的await component.ngOnInit()调用:

修改后的测试用例:

import { fakeAsync, tick } from '@angular/core/testing';

// ... 其他代码

it('should handle filter changes', fakeAsync(() => {
    const filterValue = 'test';
    mockFilterElement.nativeElement.value = filterValue;

    const event = new Event('keyup');
    mockFilterElement.nativeElement.dispatchEvent(event);

    // 等待防抖时间结束,触发订阅回调
    tick(150);
    // 触发变更检测确保组件状态同步(按需添加)
    fixture.detectChanges();

    expect(component.dataSource.filter).toBe(filterValue);
}));

内容的提问来源于stack exchange,提问作者Kenzo_Gilead

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 20:20:08