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

Angular中NgbModal模拟异常,‘Modal Opened’测试失败求助

Angular测试NgbModal模拟失败的解决思路

核心问题原因

你的DeleteModalComponent在@Component元数据的providers数组里声明了NgbModalConfig和NgbModal,这会导致组件每次实例化时都会创建一个局部独立的NgbModal实例,完全绕过你在TestBed中配置的模拟对象。测试时你spy的modalService和组件实际调用的根本不是同一个实例,所以才会出现“open未被调用”的测试失败,同时也无法返回预期的modalRef结果。


解决步骤

1. 移除组件自身的NgbModal依赖声明

修改DeleteModalComponent的@Component装饰器,删除providers里的NgbModalConfig和NgbModal:

@Component({
  selector: 'app-delete-modal',
  templateUrl: './delete-modal.component.html',
  styleUrls: ['./delete-modal.component.css'],
  // 删除此行:providers: [NgbModalConfig, NgbModal],
})
export class DeleteModalComponent {
  @Output()
  readonly notify: EventEmitter<boolean> = new EventEmitter<boolean>();

  constructor(private modalService: NgbModal, private toastr: ToastrService) { }

  open() {
    const modalRef: NgbModalRef = this.modalService.open(GroupDeleteModalComponent);
    modalRef.result.then((value) => {
      this.notify.emit(value);
    }).catch((error) => {
      console.error(error);
      this.toastr.error('Delete failed.');
    });
  }
}

理由:NgbModal属于全局服务,应该由根模块(如AppModule)统一提供,组件自行声明会打破依赖注入的层级,导致测试无法替换真实实例。

2. 在TestBed中创建并提供NgbModal的Spy对象

在测试的beforeEach(async)块中,直接创建模拟的NgbModal对象,并配置其open方法的返回值:

beforeEach(async () => {
  const toastSpy = jasmine.createSpyObj('ToastrService', ['error']);
  
  // 创建NgbModal的Spy对象,模拟open方法
  const modalSpy = jasmine.createSpyObj('NgbModal', ['open']);
  // 模拟modalRef的result属性,根据测试需求返回resolve或reject
  const mockModalRef = {
    result: Promise.resolve(true) // 测试成功分支用resolve,失败分支用Promise.reject('cancel')
  } as NgbModalRef;
  modalSpy.open.and.returnValue(mockModalRef);

  await TestBed.configureTestingModule({
    declarations: [
      DeleteModalComponent,
      GroupDeleteModalComponent,
    ],
    imports: [
      HttpClientTestingModule,
      RouterTestingModule.withRoutes([]),
      // 移除NgbModule,因为我们使用模拟对象而非真实模块
    ],
    providers: [
      NgbActiveModal,
      { provide: ToastrService, useValue: toastSpy },
      { provide: NgbModal, useValue: modalSpy }, // 注入模拟的NgbModal
    ],
  }).overrideModule(BrowserDynamicTestingModule, {
    set: {
      entryComponents: [GroupDeleteModalComponent],
    },
  }).compileComponents();
});

3. 调整测试初始化逻辑

在第二个beforeEach中,直接获取TestBed提供的Spy对象,无需额外重复spy:

beforeEach(() => {
  modalService = TestBed.inject(NgbModal) as jasmine.SpyObj<NgbModal>;
  spyOn(console, 'log').and.callThrough();
  fixture = TestBed.createComponent(DeleteModalComponent);
  component = fixture.componentInstance;
  fixture.detectChanges();
});

4. 验证测试用例

现在运行Modal Opened测试用例,就能正确检测到modalService.open被调用。如果需要测试result的then或catch分支,只需修改mockModalRef.result的值即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 17:55:18