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

如何用Jasmine测试NgbModal closed Observable中的回调函数

问题描述

我正在开发基于Angular 14的项目,使用ng-bootstrap组件,测试框架为Jasmine。代码中的openModalGroup函数用于打开模态框添加分组(标签),模态框关闭时会调用refreshRecipients刷新收件人列表。目前Sonar显示该函数中refreshRecipients的调用未被测试覆盖,是组件最后几个未覆盖的代码点之一。

我尝试编写了如下测试代码:

fit('should close modal and refresh recipients', () => {
  const modalgroupRef = modalService.open(ModalGroupComponent, {windowClass: 'modal-default-form'});
  modalgroupRef.componentInstance.groups = groups;

  spyOn(modalgroupRef, 'close');
  spyOn(component, 'refreshRecipients');

  modalgroupRef.close();
  fixture.detectChanges();

  expect(modalgroupRef.close).toHaveBeenCalled();
  expect(component.refreshRecipients).toHaveBeenCalled();
})

我认为触发模态框关闭后,closed Observable应触发回调调用refreshRecipients,但测试未检测到该函数的调用。我还尝试过其他方法,比如在测试中订阅closed,但仍未成功检测到调用。

请问在现有实现下能否验证refreshRecipients是否被成功调用?还是需要重构代码以提升可测试性?


解决方案

你的测试没命中核心逻辑——你直接调用了modalgroupRef.close(),但这个模态框实例并不是组件里openModalGroup函数创建的那个,而且你没有模拟openModalGroup内部对closed Observable的订阅逻辑。

现有实现下的正确测试方式

首先,你需要模拟NgbModal的open方法,让它返回一个带有closed Observable的模拟对象,然后调用组件的openModalGroup函数,再手动触发closed流的发射,这样才能触发回调里的refreshRecipients调用。

示例代码:

it('should call refreshRecipients when modal closes', () => {
  // 1. 创建模拟的closed Observable,用Subject来手动控制发射
  const modalClosedSubject = new Subject<void>();
  const mockModalRef = {
    componentInstance: { groups: [] },
    closed: modalClosedSubject.asObservable()
  } as jasmine.SpyObj<NgbModalRef>;

  // 2. 模拟modalService.open返回这个mock实例
  spyOn(modalService, 'open').and.returnValue(mockModalRef);
  // 3. 监听refreshRecipients
  spyOn(component, 'refreshRecipients');

  // 4. 调用组件的openModalGroup函数(这才是你要测试的目标函数)
  component.openModalGroup();

  // 5. 手动触发模态框关闭事件
  modalClosedSubject.next();
  fixture.detectChanges();

  // 6. 验证refreshRecipients被调用
  expect(component.refreshRecipients).toHaveBeenCalled();
});

为什么你的原测试失败?

  • 你直接创建了modalService.open的实例,但组件的openModalGroup函数会自己调用modalService.open,两者不是同一个实例,所以你触发的close事件和组件订阅的流完全无关。
  • 你没有调用组件的openModalGroup,等于跳过了核心的订阅逻辑,自然不会触发refreshRecipients。

是否需要重构?

如果你的openModalGroup逻辑是标准的ng-bootstrap模态框用法(比如:

openModalGroup() {
  const modalRef = this.modalService.open(ModalGroupComponent);
  modalRef.componentInstance.groups = this.groups;
  modalRef.closed.subscribe(() => {
    this.refreshRecipients();
  });
}

),那不需要重构,用上面的测试方法就能覆盖到refreshRecipients的调用逻辑。如果你的逻辑更复杂(比如嵌套了多层订阅、或者把订阅逻辑藏在了其他地方),可以考虑把订阅逻辑抽成单独的方法,方便测试,但当前场景下完全不需要。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 03:05:17