单元测试报错:调用Subject的.pipe方法提示pipe不是函数
单元测试报错:panelService.outputEmitter.pipe is not a function
报错信息
this.panelService.outputEmitter.pipe is not a function
相关代码
TypeScript业务代码
constructor(private panelService: RightPanelService) { } bulkAddCustomCapacitySchedule(roomIds) { this.panelService.create(CreateCustomCapacityScheduleComponent, { title: 'Add', subTitle: 'Custom Capacity Schedules', theme: 'success', emitterMethod: 'customCapacityScheduleSaveEdit', componentParams: { roomIdsArr: roomIds } }); this.panelService.outputEmitter.pipe(takeUntil(this.outputSubscription)).subscribe((event: FilterTagModel) => { this.searchRoomConfiguration(); }); }
RightPanelService代码
import { Injectable } from '@angular/core'; import { Subject, BehaviorSubject } from 'rxjs'; import { PanelOptionsModel } from './panel-options.model'; @Injectable() export class RightPanelService { panel = new Subject(); loadingPanel = new BehaviorSubject<boolean>(true); outputEmitter = new Subject(); panelContent: any = { isShowing: false, content: null, contentParams: PanelOptionsModel }; constructor() { } public create(component: any, parameter?: PanelOptionsModel): any { this.panelContent.isShowing = true; this.panelContent.content = component; this.panelContent.contentParams = parameter; this.panel.next(this.panelContent); } }
测试用例代码
import { RoomConfigurationListComponent } from './room-configuration-list.component'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { RightPanelService } from '../../../controls/right-panel/right-panel.service'; import { FormsModule } from '@angular/forms'; import { of } from 'rxjs'; describe('RoomConfigurationListComponent', () => { let fixture: ComponentFixture<RoomConfigurationListComponent>; let mockPanelService, mockPreRequisiteService; let mockTagDropDownData; let mockRoomListData; let component: RoomConfigurationListComponent; beforeEach(async () => { mockPanelService = jasmine.createSpyObj(['showLoading', 'create', 'outputEmitter']); await TestBed.configureTestingModule({ declarations: [RoomConfigurationListComponent], imports: [FormsModule], schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [{ provide: RightPanelService, useValue: mockPanelService }] }).compileComponents(); fixture = TestBed.createComponent(RoomConfigurationListComponent); component = fixture.componentInstance; }); it('should call onBulkActionClick method', () => { spyOn(fixture.componentInstance, 'onBulkActionClick').and.callThrough(); mockPreRequisiteService.GetDropdownsData.and.returnValue(of(mockTagDropDownData)); component.selectedRows = mockRoomListData.items; fixture.detectChanges(); fixture.componentInstance.onBulkActionClick(); expect(fixture.componentInstance.onBulkActionClick).toHaveBeenCalled(); }); });
错误原因
测试用例中,你用jasmine.createSpyObj创建mockPanelService时,错误地将outputEmitter作为方法名传入。但实际outputEmitter是RightPanelService的属性(RxJS Subject实例),createSpyObj会把传入的字符串全部创建为spy函数,导致mockPanelService.outputEmitter变成了一个没有pipe方法的普通函数,而非Observable类型,因此调用pipe时触发报错。
解决办法
以下两种方式均可修复:
方法1:手动添加正确的outputEmitter属性
先创建仅包含方法的spy对象,再手动为其添加outputEmitter属性并赋值为RxJS Subject(或Observable):
beforeEach(async () => { // 只创建方法的spy,不包含outputEmitter mockPanelService = jasmine.createSpyObj(['showLoading', 'create']); // 手动添加属性,用Subject模拟真实服务的outputEmitter mockPanelService.outputEmitter = new Subject(); await TestBed.configureTestingModule({ declarations: [RoomConfigurationListComponent], imports: [FormsModule], schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [{ provide: RightPanelService, useValue: mockPanelService }] }).compileComponents(); fixture = TestBed.createComponent(RoomConfigurationListComponent); component = fixture.componentInstance; });
方法2:使用Jasmine的属性配置(Jasmine 3.4+)
如果你的Jasmine版本≥3.4,可以直接在创建spy对象时同时指定方法和属性:
beforeEach(async () => { mockPanelService = jasmine.createSpyObj( 'RightPanelService', ['showLoading', 'create'], // 要模拟的方法数组 { outputEmitter: new Subject() } // 要模拟的属性对象 ); await TestBed.configureTestingModule({ // 其余配置不变 }).compileComponents(); // 其余代码不变 });
如果不需要实际执行订阅逻辑,也可以用of()创建空Observable替代Subject:
mockPanelService.outputEmitter = of(null);
内容的提问来源于stack exchange,提问作者Sandip - Frontend Developer
相关产品推荐
相关产品推荐

