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

如何用Jasmine spyOn PrimeNG MessageService测试组件错误提示?

问题:测试PrimeNG MessageService错误消息触发失败

组件代码

@Component({
  selector: 'app-get-menu',
  templateUrl: './get-menu.component.html',
  styleUrls: ['./get-menu.component.scss'],
  providers: [MessageService], // 核心问题点
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  imports: [Toast],
})
export class GetMenuComponent implements OnInit {

  menu: MenuInterface[] = [];

  constructor(
    private apiService: ApiService,
    private router: Router,
    private dateTransformerService: DateTransformerService,
    private messageService: MessageService
  ) {}

  ngOnInit() {
    this.initData();
  }

  initData() {
    this.apiService.getMenu().subscribe({
      next: (menu) => {
        this.menu = menu;
      },
      error: (error) => {
        this.messageService.add({
          severity: "error",
          summary: "Error",
          detail: "Couldn't load menu",
          life: 10000,
        });
      }
    });
  }

测试代码

describe('GetMenuComponent', () => {

  let component: GetMenuComponent;
  let fixture: ComponentFixture<GetMenuComponent>;
  let apiService: ApiService;
  let messageService: MessageService;
  let router: Router;
  let dateTransformerService: DateTransformerService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [GetMenuComponent],
      providers: [ApiService,MessageService, provideHttpClient(), 
                  provideHttpClientTesting(), Router, DateTransformerService]
    });
    fixture = TestBed.createComponent(GetMenuComponent);
    fixture.detectChanges();
    component = fixture.componentInstance;

    apiService = TestBed.inject(ApiService);
    router = TestBed.inject(Router);
    dateTransformerService = TestBed.inject(DateTransformerService);
    messageService = TestBed.inject(MessageService);
  });

  it("Should handle errors and show error message", fakeAsync(() => {
    spyOn(apiService, 'getMenu').and.returnValue(throwError(() => new Error("some error")));
    spyOn(messageService, 'add');
    component.initData();

    tick(200);
    expect(messageService.add).toHaveBeenCalled();
  }))

错误信息

Expected spy add to have been called.

原因分析

你在组件的@Component装饰器中声明了providers: [MessageService],这会让每个组件实例拥有独立的MessageService实例。而测试中通过TestBed.inject(MessageService)获取的是TestBed根注入器的实例,和组件内部实际使用的不是同一个对象,所以你对这个实例的spy不会被组件的错误回调触发。


解决方法

方法1:在测试中覆盖组件的MessageService提供者

创建一个可spy的Mock实例,在TestBed配置中替换掉组件的MessageService:

beforeEach(() => {
  // 创建Mock MessageService
  const mockMessageService = jasmine.createSpyObj('MessageService', ['add']);

  TestBed.configureTestingModule({
    imports: [GetMenuComponent],
    providers: [
      ApiService,
      provideHttpClient(), 
      provideHttpClientTesting(),
      Router,
      DateTransformerService,
      // 覆盖组件的MessageService提供者
      { provide: MessageService, useValue: mockMessageService }
    ]
  });
  
  fixture = TestBed.createComponent(GetMenuComponent);
  component = fixture.componentInstance;
  
  apiService = TestBed.inject(ApiService);
  messageService = TestBed.inject(MessageService); // 现在拿到的是Mock实例
});

it("Should handle errors and show error message", fakeAsync(() => {
  spyOn(apiService, 'getMenu').and.returnValue(throwError(() => new Error("some error")));
  
  component.initData();
  tick(); // 触发所有异步操作
  
  // 建议断言具体参数,测试更严谨
  expect(messageService.add).toHaveBeenCalledWith({
    severity: "error",
    summary: "Error",
    detail: "Couldn't load menu",
    life: 10000,
  });
}))

方法2:从组件的注入器获取实例

组件自身的注入器中保存着它专属的MessageService实例,直接从这里获取并spy:

beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [GetMenuComponent],
    providers: [ApiService, provideHttpClient(), provideHttpClientTesting(), Router, DateTransformerService]
  });
  
  fixture = TestBed.createComponent(GetMenuComponent);
  component = fixture.componentInstance;
  
  apiService = TestBed.inject(ApiService);
  // 从组件注入器获取实例,而非TestBed根注入器
  messageService = fixture.debugElement.injector.get(MessageService);
});

it("Should handle errors and show error message", fakeAsync(() => {
  spyOn(apiService, 'getMenu').and.returnValue(throwError(() => new Error("some error")));
  spyOn(messageService, 'add');
  
  component.initData();
  tick();
  
  expect(messageService.add).toHaveBeenCalled();
}))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:33:21