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

Angular使用Jasmine&Karma编写路由跳转、Material弹窗点击事件单元测试

Angular Jasmine & Karma 单元测试问题解答

1. 测试调用router.navigateByUrl的点击事件

直接用Mock的Router服务做测试即可,不需要引入真实路由模块,测试逻辑如下:

  • 用jasmine.createSpyObj创建带navigateByUrl间谍方法的Router mock对象
  • 在测试模块的providers中替换真实Router为mock对象
  • 触发onClickCancel方法后断言间谍方法调用符合预期

完整测试代码示例:

import { TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { YourComponent } from './your.component';

// 创建Router mock对象
const mockRouter = jasmine.createSpyObj('Router', ['navigateByUrl']);

describe('YourComponent', () => {
  let component: YourComponent;
  let router: Router;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [YourComponent],
      providers: [
        { provide: Router, useValue: mockRouter }
      ]
    });
    component = TestBed.createComponent(YourComponent).componentInstance;
    router = TestBed.inject(Router);
  });

  it('should navigate to login page when onClickCancel is called', () => {
    component.onClickCancel();
    // 断言navigateByUrl被调用1次,且入参为'login'
    expect(router.navigateByUrl).toHaveBeenCalledOnceWith('login');
  });
});

2. 测试Angular Material Dialog调用及报错解决

报错原因说明

TypeError: Cannot read property 'count' of undefined 错误的核心原因是dialog.open没有被正确封装为Jasmine间谍对象,只有经过spyOn或者jasmine.createSpy创建的方法才会携带calls属性。

完整测试实现

需要同时mock MatDialog和DialogRef对象,保证afterClosed方法返回可观察对象,测试代码如下:

import { TestBed, fakeAsync, tick } from '@angular/core/testing';
import { MatDialog } from '@angular/material/dialog';
import { of } from 'rxjs';
import { YourComponent } from './your.component';
import { AddNewRoleComponent } from './add-new-role.component';

//  mock DialogRef对象,提供afterClosed方法
const mockDialogRef = {
  afterClosed: () => of(null)
};
// 创建带open方法间谍的MatDialog mock对象
const mockDialog = jasmine.createSpyObj('MatDialog', ['open']);

describe('YourComponent', () => {
  let component: YourComponent;
  let dialog: MatDialog;

  beforeEach(() => {
    // 每次测试前重置间谍调用记录,避免用例间相互干扰
    mockDialog.open.calls.reset();
    mockDialog.open.and.returnValue(mockDialogRef);
    // 为getRoles创建间谍,方便后续断言
    spyOn(component, 'getRoles');

    TestBed.configureTestingModule({
      declarations: [YourComponent],
      providers: [
        { provide: MatDialog, useValue: mockDialog }
      ]
    });
    component = TestBed.createComponent(YourComponent).componentInstance;
    dialog = TestBed.inject(MatDialog);
  });

  // 测试addNewRole方法
  it('should open AddNewRoleComponent with correct config when addNewRole is called', () => {
    component.addNewRole();
    expect(dialog.open).toHaveBeenCalledOnceWith(AddNewRoleComponent, {
      width: '450px',
      disableClose: true
    });
    // 也可以用calls.count()写法断言调用次数
    // expect(dialog.open.calls.count()).toBe(1);
  });

  // 测试editRole方法
  it('should open AddNewRoleComponent with role data when editRole is called', () => {
    const testRole = { id: 1, name: '测试角色' };
    component.editRole(testRole);
    expect(dialog.open).toHaveBeenCalledOnceWith(AddNewRoleComponent, {
      width: '450px',
      disableClose: true,
      data: testRole
    });
  });

  // 测试弹窗关闭后触发getRoles
  it('should call getRoles after dialog closed', fakeAsync(() => {
    component.addNewRole();
    tick(); // 触发afterClosed的Observable订阅逻辑
    expect(component.getRoles).toHaveBeenCalled();
  }));
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 05:24:01