如何用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
相关产品推荐
相关产品推荐

