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

Angular中使用Jasmine测试Ionic Modal无法访问内部组件问题

Angular与Ionic Modal集成测试问题解决

业务实现代码

宿主页面打开Modal、传参、接收回传数据的核心代码如下:

const modal = await this.modalController.create({
  component: EditPositionModalComponent,
  componentProps: {orderPosition}
});
await modal.present();

// 等待Modal关闭,处理回传数据
let {data} = await modal.onDidDismiss();

测试目标

本次集成测试需要覆盖两个验证点:

  • 传入Modal的orderPosition初始数据准确无误
  • Modal关闭后,用户编辑后的修改数据可正确回传至宿主页面

现有测试代码与问题

当前已编写的测试基础代码如下:

describe('OrderDetailPage/EditPosition Integrationtest', async () => {
  let component: OrderDetailPage;
  let fixture: ComponentFixture<OrderDetailPage>;
  let modalControllerSpy : Spy;

  beforeEach((() => {
    TestBed.configureTestingModule({
      declarations: [OrderDetailPage],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      imports: [
        CommonModule,
        IonicModule.forRoot(),
        HttpClientTestingModule,
        ReactiveComponentModule,
        ReactiveFormsModule,
        ComponentsModule,
        RouterTestingModule,
        TranslateModule.forRoot()],
      providers: [
        provideMockStore({}),
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(OrderDetailPage);
    component = fixture.componentInstance;
    fixture.detectChanges();

    modalControllerSpy = spyOn(ModalController.prototype, 'create').and.callThrough();
    component.onEditPosition(new MouseEvent(''), testOrderPosition); // 触发打开Modal的点击事件
    fixture.detectChanges();
  }));

  it('should send correct data to modal', async () => {
    let modal: HTMLIonModalElement = await modalControllerSpy.calls.first().returnValue;
    await modal.present();
  });
});

目前测试中可通过spy获取modalController创建的HTMLIonModalElement实例,但始终无法访问Modal承载的EditPositionModalComponent组件实例。即使等待modal.present()执行完成、使用fakeAsync的tick方法推进异步任务队列,Modal内部依然没有可用于测试的有效内容,当前获取到的Modal DOM结构如下:

<ion-modal class="md modal-default hydrated" id="ion-overlay-1" no-router="" aria-modal="true" tabindex="-1" style="z-index: 20001;">
   <app-edit-position-modal _nghost-a-c317="" class="ion-page">
     <!--container--><!--container-->
   </app-edit-position-modal>
   <app-edit-position-modal _nghost-a-c317="" class="ion-page">
     <!--container--><!--container-->
   </app-edit-position-modal>
</ion-modal>

解决方案

1. 补全TestBed模块配置

当前测试模块的declarations数组中仅声明了宿主页面OrderDetailPage,Ionic Modal为动态创建组件,未在测试模块声明的组件无法正常完成实例初始化,需要将EditPositionModalComponent加入declarations数组,同时补充该组件依赖的所有服务Mock。

2. 直接通过Modal实例属性获取内部组件

DOM中出现两个重复的app-edit-position-modal节点是Ionic动画过渡的预渲染占位节点,没有挂载实际组件实例,不需要通过DOM查询获取组件。await modal.present()执行完成后,等待Angular变更检测队列清空,可直接通过Modal实例自带的componentInstance属性拿到内部组件的有效引用,验证传参正确性的测试代码示例:

it('should send correct data to modal', async () => {
  const modal: HTMLIonModalElement = await modalControllerSpy.calls.first().returnValue;
  await modal.present();
  // 等待所有异步任务、变更检测执行完成
  await fixture.whenStable();
  fixture.detectChanges();

  const modalComp = modal.componentInstance as EditPositionModalComponent;
  // 断言传入的初始数据正确
  expect(modalComp.orderPosition).toEqual(testOrderPosition);
});

3. 验证数据回传逻辑

不需要在测试中模拟用户在Modal内的完整输入操作,可直接调用Modal实例的dismiss方法传入模拟的修改后数据,验证宿主页面的接收处理逻辑是否正常,代码示例:

it('should receive edited data after modal dismiss', async () => {
  const modal: HTMLIonModalElement = await modalControllerSpy.calls.first().returnValue;
  await modal.present();
  await fixture.whenStable();
  fixture.detectChanges();

  const mockUpdatedPosition = { /* 模拟用户编辑后的订单位置数据 */ };
  // 模拟Modal提交后关闭,传入修改后的数据
  await modal.dismiss(mockUpdatedPosition);
  await fixture.whenStable();
  fixture.detectChanges();

  // 断言宿主页面正确接收并处理了回传数据,根据实际业务逻辑调整断言字段
  expect(component.currentOrderPosition).toEqual(mockUpdatedPosition);
});

注意事项

  • 对modalController.create的spy必须保留and.callThrough(),如果自行mock返回值需要保证返回真实的Ionic Modal实例引用,否则无法拿到componentInstance
  • 所有异步操作(present、dismiss)执行后都要调用await fixture.whenStable()并触发一次detectChanges,等Angular完成动态组件创建、变更检测后再执行断言,不要在异步方法resolve后立刻读取实例属性,避免拿到未初始化的空对象

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 02:45:51