Angular单元测试中AutoScrollDirective内setTimeout回调未触发问题
问题根因
- 测试执行时序错误:
beforeEach中提前执行了fixture.detectChanges(),此时还未对scrollIntoView做Mock,且已经触发了指令的ngAfterViewInit完成了setTimeout注册,后续在测试用例中的操作已经错过执行时机。 - Mock时机过晚:第一次变更检测触发指令生命周期注册
setTimeout时,scrollIntoView还未被Mock,回调执行时调用的是原生方法,不会触发你在测试用例中后定义的Mock函数。 - 手动调用测试组件的
ngAfterViewInit无效:该方法是测试组件自身的生命周期钩子,调用它不会触发挂载在模板内元素上的指令的生命周期,指令的生命周期完全由Angular变更检测自动调度。 tick()时间参数缺失:默认tick()只推进0ms时间,若要匹配指令默认100ms的延时,需要传入对应时间参数,或直接使用flush()清空所有待执行的定时器任务。
修正后的测试方案
最优实现(避免污染全局原型)
@Component({ template: `<div appAutoScroll></div>`, }) class TestAutoScrollDirectiveComponent {} describe('AutoScrollDirective', () => { let fixture: ComponentFixture<TestAutoScrollDirectiveComponent>; let scrollIntoViewMock: jest.Mock; let originScrollIntoView: typeof HTMLElement.prototype.scrollIntoView; beforeEach(() => { TestBed.configureTestingModule({ declarations: [AutoScrollDirective, TestAutoScrollDirectiveComponent], imports: [BrowserDynamicTestingModule], }); // 提前创建组件实例,但不触发变更检测 fixture = TestBed.createComponent(TestAutoScrollDirectiveComponent); // 先完成Mock再触发生命周期 originScrollIntoView = HTMLElement.prototype.scrollIntoView; scrollIntoViewMock = jest.fn(); HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; }); // 测试结束后恢复原型,避免污染全局影响其他用例 afterEach(() => { HTMLElement.prototype.scrollIntoView = originScrollIntoView; }); it('should call scrollIntoView of the element with default delay', fakeAsync(() => { // 第一次触发变更检测,此时才会执行指令的ngAfterViewInit注册定时器 fixture.detectChanges(); // 推进100ms时间匹配默认delay,也可直接用flush()清空所有定时器 tick(100); expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: 'smooth', block: 'start', inline: 'nearest' }); })); // 可扩展自定义delay的测试场景 it('should call scrollIntoView with custom delay', fakeAsync(() => { // 修改测试组件模板传入自定义delay,后续逻辑同上述用例即可 })); });
优化点说明
- 把第一次
fixture.detectChanges()移到测试用例内,Mock完成后再触发变更检测,保证指令执行时Mock已经生效 - 移除了无效的手动调用测试组件
ngAfterViewInit的逻辑 - 补充了测试完成后的原型恢复逻辑,避免全局污染影响其他测试用例
- 不仅校验方法是否调用,还校验了传入的参数是否符合预期,测试覆盖更完整
- 可直接扩展自定义delay的测试场景,复用性更强
内容的提问来源于stack exchange,提问作者PyDario
相关产品推荐
相关产品推荐

