Angular单元测试:使用Jasmine模拟RxJS Observable遇pipe未定义问题
问题根因
Cannot read property 'pipe' of undefined 报错的核心是你调用 this._service.getAllObservableAsync() 时方法返回了undefined,没有返回预期的Observable对象,具体有3个核心错误点:
- 方法执行时机顺序错误:组件的
_getAllProducts是在构造函数里调用的,而你在测试中是先调用TestBed.createComponent(AppComponent)创建组件(构造函数已经执行),之后才给getAllObservableAsyncspy设置返回值,此时构造函数执行时spy还没有返回值,默认返回undefined - Stub类名不匹配:你的stub文件导出的是
ProductServiceStub,但测试中声明使用的是ThingServiceStub,类名不对应 - 辅助错误:模拟数据和断言不匹配:你调用
getThingsSubject.next()时没有传入模拟的IThing数组,且子组件输入属性是[thing],但测试断言的是product属性,两者不对应
修复步骤
1. 调整测试执行顺序
把给spy设置返回值的逻辑,放到TestBed.createComponent执行之前,保证组件构造函数执行时spy已经有正确的返回值:
// 修改第二个beforeEach逻辑 beforeEach(() => { // 先给spy设置返回值,再创建组件 getThingsSubject = new Subject(); (dependencies.thingService .getAllObservableAsync as jasmine.Spy).and.returnValue( getThingsSubject.asObservable() ); fixture = TestBed.createComponent(AppComponent); component = fixture.componentInstance; fixture.detectChanges(); }); // 原on initialisation分组里的重复spy设置逻辑可以删除
2. 修正Stub类名
把thing.service.stub.ts的类名改成和测试一致的ThingServiceStub:
export class ThingServiceStub { public getAllObservableAsync: jasmine.Spy = jasmine.createSpy('getAllObservableAsync'); }
3. 补充模拟数据,修正断言
给next传入模拟数组,同时断言匹配子组件的输入属性名:
describe('when the things have been fetched', () => { const mockThings = [{ name: 'product', number: '1' }]; beforeEach(fakeAsync(() => { // 传入模拟数据 getThingsSubject.next(mockThings); tick(); fixture.detectChanges(); })); it('should display the things', () => { // 断言thing属性而不是product expect(getThings()[0].componentInstance.thing).toEqual(mockThings[0]); }); });
4. 可选优化:把构造函数的初始化逻辑移到ngOnInit
Angular官方不推荐在构造函数里执行业务逻辑,建议移到ngOnInit生命周期钩子,更方便测试控制执行时机:
// AppComponent.ts export class AppComponent implements OnInit { public errorObjectSubject = null; public thingsSubject: Subject<Array<IThing>> = new Subject(); constructor(private readonly _service: ThingService) {} ngOnInit(): void { this._getAllProducts(); } // 其余逻辑不变 }
内容的提问来源于stack exchange,提问作者Winthorpe
相关产品推荐
相关产品推荐

