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

Angular独立组件单元测试中Jest Mock未正确生效问题排查

问题

我有一个简单的Ionic/Angular独立组件,已精简无关代码。组件依赖ModalController,点击关闭按钮会调用其dismiss方法。编写Jest测试时模拟了ModalController,但测试断言dismiss被调用失败,调试发现mock未被正确使用,此前同类mock在其他测试中有效,请问遗漏了什么?

组件代码

import { IonicModule, ModalController } from '@ionic/angular';

@Component({
  selector: 'my-component',
  templateUrl: 'my-component.html',
  styleUrls: ['my-component.scss'],
  standalone: true,
  imports: [
    IonicModule
  ]
})

export class MyComponent implements OnInit {
  @Input() inputParam: Organization;

  public paramOne: string;

  constructor(private modalCtrl: ModalController) {}

  ngOnInit(): void {
    this.paramOne = this.inputParam?.id;
  }

  onClickClose(): void {
    this.modalCtrl.dismiss();
  }
}

测试代码

import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ModalController } from '@ionic/angular';
import { MyComponent } from './my.component';

describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;
  let ionicModalControllerMock = {
     dismiss: jest.fn()
  };

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [MyComponent, NoopAnimationsModule],
      providers: [{ provide: ModalController, useValue: ionicModalControllerMock }]
    }).compileComponents();
  });

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

   it('should dismiss the modal without data on close click', () => {
     component.onClickClose();
     expect(ionicModalControllerMock.dismiss).toHaveBeenCalledWith();
   });
});
解决方案

问题出在独立组件的依赖注入优先级上:你的组件是standalone: true,且已经导入了IonicModule,而IonicModule内部会提供ModalController的真实实例。此时你在TestBed.configureTestingModule的根级providers中配置的mock会被组件自身导入模块提供的服务覆盖,导致mock未生效。

修复方法有两种,任选其一即可:

方法一:用withProviders覆盖组件的依赖

修改TestBed的imports配置,通过独立组件的withProviders方法直接为组件注入mock:

beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [
      MyComponent.withProviders([{ provide: ModalController, useValue: ionicModalControllerMock }]),
      NoopAnimationsModule
    ]
  }).compileComponents();
});

方法二:用overrideComponent替换组件的providers

在TestBed配置中,通过overrideComponent直接修改组件的依赖提供:

beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [MyComponent, NoopAnimationsModule]
  })
  .overrideComponent(MyComponent, {
    set: {
      providers: [{ provide: ModalController, useValue: ionicModalControllerMock }]
    }
  })
  .compileComponents();
});

原理说明:独立组件的依赖注入优先级顺序为组件自身的providers > 导入模块的providers > TestBed根级providers。因此必须直接在组件层级替换ModalController的提供方,才能确保mock被正确注入。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:32:38