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

如何用Jest/Angular Testing Library测试EventEmitter订阅分支?

测试覆盖方案&参考资料

一、覆盖订阅逻辑与if分支的测试代码实现

1. 搭建测试基础环境

首先需要模拟NgbModalService和模态框实例,无需真实渲染组件,只需验证逻辑交互即可。以下是完整测试框架:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { YourComponent } from './your.component'; // 替换为你的组件类名
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { EventEmitter } from '@angular/core';

// 自定义模拟模态框引用类
class MockModalRef implements Partial<NgbModalRef> {
  componentInstance = {
    confirmMessage: '',
    emitService: new EventEmitter<boolean>()
  };
}

describe('YourComponent', () => {
  let component: YourComponent;
  let modalService: jest.Mocked<NgbModal>;
  let mockModalRef: MockModalRef;

  beforeEach(async () => {
    // 模拟NgbModal服务
    const modalMock = {
      open: jest.fn(() => new MockModalRef())
    };

    await TestBed.configureTestingModule({
      declarations: [YourComponent],
      providers: [{ provide: NgbModal, useValue: modalMock }]
    }).compileComponents();

    component = TestBed.createComponent(YourComponent).componentInstance;
    modalService = TestBed.inject(NgbModal) as jest.Mocked<NgbModal>;
    mockModalRef = modalService.open.mock.results[0].value as MockModalRef;
    // 提前mock删除方法,方便后续验证调用
    jest.spyOn(component, 'deleteIndividualCertById').mockImplementation(() => {});
  });

2. 测试确认删除分支(触发if条件)

该测试覆盖用户确认删除的场景,验证订阅事件触发后删除方法是否被正确调用:

it('calls deleteIndividualCertById when delete is confirmed', () => {
    const testId = 123;
    component.openDeleteModal(testId);

    // 模拟模态框的确认事件
    mockModalRef.componentInstance.emitService.next(true);

    // 验证核心逻辑
    expect(component.deleteIndividualCertById).toHaveBeenCalledWith(testId);
    expect(modalService.open).toHaveBeenCalledWith(
      expect.any(Function), // 对应DeleteConfirmComponent
      { size: 'lg', backdrop: 'static', centered: true }
    );
    expect(mockModalRef.componentInstance.confirmMessage).toBe(
      "Are you sure you wish to delete this Certification?"
    );
  });

3. 测试取消删除分支(不触发if条件)

该测试覆盖用户取消删除的场景,验证删除方法不会被调用:

it('does NOT call deleteIndividualCertById when delete is cancelled', () => {
    const testId = 456;
    component.openDeleteModal(testId);

    // 模拟模态框的取消事件
    mockModalRef.componentInstance.emitService.next(false);

    // 验证删除方法未被调用
    expect(component.deleteIndividualCertById).not.toHaveBeenCalled();
  });
});

以上代码可完整覆盖openDeleteModal中的订阅逻辑和if分支的两种情况。

二、Jest + Angular Testing Library 实用参考要点

核心原则

始终围绕用户真实操作编写测试,而非直接调用组件方法(纯逻辑方法除外)。例如组件中有触发openDeleteModal的“删除”按钮时,可模拟真实点击流程:

import { render, screen } from '@testing-library/angular';

it('triggers delete flow when delete button is clicked', async () => {
  // 渲染组件
  await render(YourComponent);
  // 点击页面上的删除按钮
  await screen.getByRole('button', { name: /delete certification/i }).click();
  // 点击模态框内的确认按钮
  await screen.getByRole('button', { name: /confirm/i }).click();
  // 验证删除方法被调用
  expect(component.deleteIndividualCertById).toHaveBeenCalled();
});

Jest 实用技巧

  • 用jest.spyOn快速模拟组件或服务方法,可通过mockRestore()恢复原始实现
  • 用jest.mock批量模拟第三方库(如@ng-bootstrap/ng-bootstrap),减少重复配置
  • 在beforeEach中用jest.clearAllMocks()清除mock调用记录,避免测试间污染

Angular Testing Library 关键方法

  • screen.getByRole:按角色查询DOM元素(优先使用,符合无障碍测试标准)
  • screen.getByText:按文本内容查询元素
  • await ...click():模拟用户点击操作,自动等待页面状态更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 23:25:27