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

Angular12升级后Karma测试SpyOn监听私有方法内服务调用失效问题

问题成因

这个问题是测试代码执行顺序和Angular 12变更检测时机的变化共同导致的,核心原因是你在ngOnInit触发之后才对服务方法做Spy:

  • Angular组件的ngOnInit生命周期钩子会在首次变更检测执行时触发
  • 你现有测试代码的执行顺序是:
    1. 创建组件实例
    2. 开启fixture.autoDetectChanges() —— Angular 12中开启自动变更检测后会立刻执行首次变更检测,此时ngOnInit已经执行完成,doThis里调用的是原始的myService.confirm方法
    3. 才对MyService.confirm做Spy模拟 —— 此时Spy生效的时机晚于方法调用,自然不会触发你写的模拟逻辑
  • 你把doThis改为public后手动调用能生效,是因为手动调用发生在Spy完成之后,自然能命中模拟逻辑。另外Angular 12中废弃了TestBed.get方法,替换为TestBed.inject,原有TestBed.get的类型推导和实例获取逻辑也有小幅变动,可能会放大这个时序问题。
修复方案

调整测试代码的执行顺序,所有Spy操作要放在触发首次变更检测之前,推荐写法如下:

beforeEach(async () => {
    // 1. 先完成服务的Spy,再创建组件、触发变更检测
    const myService = TestBed.inject(MyService);
    spyOn(myService, 'confirm').and.callFake((params: Confirmation) => {
        params.reject();
    });
    // 2. 再创建组件实例
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    // 3. 最后开启自动变更检测/手动触发首次检测
    fixture.autoDetectChanges();
    await fixture.whenStable();
});

如果你的测试逻辑需要先创建组件再做Spy,也可以把开启自动变更检测的逻辑放到Spy之后:

beforeEach(async () => {
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    // 先Spy,再开启变更检测
    const myService = TestBed.inject(MyService);
    spyOn(myService, 'confirm').and.callFake((params: Confirmation) => {
        params.reject();
    });
    fixture.autoDetectChanges();
    await fixture.whenStable();
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 22:57:01