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

Angular Signal测试问题:spyOn getScrollTop未触发计算链

问题根源与修复方案

核心问题

你的测试代码中,spyOn(component, 'getScrollTop')是在fixture.detectChanges()之后执行的——而detectChanges()会触发组件初始化,此时constructor里的scrollLimit和directionScroll已经调用过getScrollTop了,后续的mock完全没生效,自然不会触发下游scrollLimit、directionScroll的日志和计算链。

具体修复步骤

  1. 提前Spy时机:必须在组件初始化(即fixture.detectChanges())之前完成getScrollTop的mock,确保组件构造时调用的是mock后的方法。
  2. 适配directionScroll的数据流要求:directionScroll里用了skip(1),意味着它需要至少两个滚动位置值才能输出结果;同时要确保返回的Observable能正确发出值,触发toSignal的更新。
  3. Mock视口初始滚动位置:directionScroll的scan初始值依赖viewportRuler.getViewportScrollPosition(),测试里需要mock这个方法,避免环境差异影响结果。

修改后的测试代码

describe('HeaderComponent', () => {
  let component: HeaderComponent;
  let fixture: ComponentFixture<HeaderComponent>;
  let scrollDispatcher: ScrollDispatcher;
  let viewportRuler: ViewportRuler;
  let sidenavOpeningService: SidenavOpeningService;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [BrowserAnimationsModule, HeaderComponent, MatIconTestingModule],
      providers: [
        { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(HeaderComponent);
    component = fixture.componentInstance;
    
    // 提前获取服务实例并mock
    scrollDispatcher = TestBed.inject(ScrollDispatcher);
    viewportRuler = TestBed.inject(ViewportRuler);
    sidenavOpeningService = TestBed.inject(SidenavOpeningService);

    // Mock视口初始滚动位置
    spyOn(viewportRuler, 'getViewportScrollPosition').and.returnValue({ top: 0, left: 0 });

    // 关键:在组件初始化前Spy getScrollTop
    const topLimit = component['topLimitShowToolbar'];
    spyOn(component, 'getScrollTop').and.returnValues(
      of(topLimit - 100),  // 给scrollLimit的流
      of(0, topLimit - 100) // 给directionScroll的流:两个值满足skip(1)的要求
    );

    // 此时再触发组件初始化
    fixture.detectChanges();
  });

  it('showToolbar should be true when scrolling before the topLimit', async () => {
    expect(component.showToolbar()).toBeTrue();
  });
});

额外说明

  • 若要测试不同滚动场景(如滚动超过阈值、向上/向下滚动),可调整getScrollTop返回的Observable序列,确保directionScroll能接收到足够的触发值。
  • 组件内的effect(() => this.showToolbar())用于强制触发Signal计算链,测试时mock正确的话,这个effect会自动响应数据流变化。

内容的提问来源于stack exchange,提问作者Burckhardt Sébastien

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 10:13:19