Angular单元测试中无法Mock BehaviorSubject的问题求助
我在Angular 16组件的ngOnInit中订阅了subscribersService的tableList$(一个BehaviorSubject):
ngOnInit() { const { pageNumber } = this.subscribersService.getMiscValues(); this.subscribersService.initPage(pageNumber); this.subscribersService.tableList$.subscribe((tableList) => { // this.tableList = this.deleteSubscribersService.setDeleteCheckboxStates(tableList); }); }
编写单元测试时尝试Mock这个BehaviorSubject,却报错:this.subscribersService.tableList$.subscribe is not a function。我也试过subscribersServiceMock.tableList$.and.returnValue({ subscribe: () => tableListMock$.subscribe });,但问题依旧。
我的测试代码如下:
fdescribe('ManageSubscribersComponent', () => { let component: ManageSubscribersComponent; let fixture: ComponentFixture<ManageSubscribersComponent>; let miscMockValues; let tableMockData; let tableListMock$; let dialogServiceMock let dialogSubjectMock; let subscribersServiceMock; let deleteSubscribersServiceMock; const displayedColumns = [ 'id', 'name', 'description', 'createdBy', 'createdOn', 'updatedBy', 'updatedOn', ]; beforeEach(() => { tableListMock$ = new Subject(); dialogSubjectMock = new Subject(); dialogServiceMock = jasmine.createSpyObj([ 'open', 'afterClosed', ]); deleteSubscribersServiceMock = jasmine.createSpyObj([ 'deleteSubscribers', ]); subscribersServiceMock = jasmine.createSpyObj([ 'resetMiscValues', 'getMiscValues', 'initPage', 'tableList$', ]); miscMockValues = { pageNumber: 1, pageSize: 25, sort: '', active: true, testMode: false, searchTerms: '', }; subscribersServiceMock.getMiscValues.and.returnValue(miscMockValues); subscribersServiceMock.tableList$.and.returnValue(tableListMock$); dialogServiceMock.afterClosed.and.returnValue(dialogSubjectMock); TestBed.configureTestingModule({ declarations: [ManageSubscribersComponent], imports: [ HttpClientTestingModule, MatFormFieldModule, MatIconModule, MatSliderModule, MatTableModule, MatFormFieldModule, MatInputModule, BrowserAnimationsModule, MatPaginatorModule, MatDialogModule, ], schemas: [ CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, ], providers: [ { provide: SubscribersService, useValue: subscribersServiceMock, }, { provide: DeleteSubscribersService, useValue: deleteSubscribersServiceMock, }, ], }); fixture = TestBed.createComponent(ManageSubscribersComponent); component = fixture.componentInstance; fixture.detectChanges(); }); fit('should init', fakeAsync(() => { fixture.whenStable().then(() => { expect(component.displayedColumns).toEqual(displayedColumns); expect(subscribersServiceMock.getMiscValues).toHaveBeenCalled(); }) })); });
问题原因
tableList$是属性(Observable/BehaviorSubject实例),不是方法,但你用jasmine.createSpyObj把它当成方法来创建Spy,导致subscribersServiceMock.tableList$变成了一个Spy函数,而非Observable对象。调用subscribe时自然会报错,因为函数本身没有这个方法。
解决方案
不要在createSpyObj的方法列表中包含tableList$,而是手动给Mock对象添加这个属性,直接赋值为你的tableListMock$(Subject实例本身就具备subscribe方法):
修改测试中的Mock创建部分:
// 创建Spy时只包含方法,去掉tableList$ subscribersServiceMock = jasmine.createSpyObj([ 'resetMiscValues', 'getMiscValues', 'initPage', ]); // 手动添加tableList$属性,赋值为你的Subject实例 subscribersServiceMock.tableList$ = tableListMock$; // 移除这行错误代码:subscribersServiceMock.tableList$.and.returnValue(tableListMock$);
这样组件中调用this.subscribersService.tableList$.subscribe时,就会正确调用Subject的subscribe方法,不会再报错。
内容的提问来源于stack exchange,提问作者dman

