Angular Jest单元测试如何验证subscribe内函数被正确调用
Angular 单元测试Observable订阅逻辑不触发的解决方案
错误原因
你当前的测试逻辑存在两个核心问题:
beforeEach阶段执行的fixture.detectChanges()会直接触发组件的ngOnInit生命周期,此时你给observable$赋值的是仅包含空subscribe方法的假对象,组件的订阅逻辑已经绑定到这个无效对象上。后续测试用例中重新给observable$赋值不会重新执行ngOnInit,新的 observable 根本没有被组件订阅。- 测试用例中你手动给新的
observable$实例加的订阅和组件本身的订阅是完全独立的两个逻辑,就算这个订阅触发了也不会调用组件的目标函数,自然会得到调用次数为0的结果。
修正方案
这里提供两种可行的写法,按需选择即可:
方案1:使用Subject模拟可手动发射值的Observable(更灵活,支持多次发射值的场景)
import { Subject } from 'rxjs'; let component: MyComponent; let fixture: ComponentFixture<MyComponent>; // 声明Subject用来控制observable$的发射行为 let mockObservableSubject: Subject<Type>; beforeEach(async () => { mockObservableSubject = new Subject<Type>(); await TestBed.configureTestingModule({ imports: [Modules], providers: [provideMock({})], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(MyComponent); component = fixture.componentInstance; // 替换组件的observable$为Subject的可观察实例 component['observable$'] = mockObservableSubject.asObservable(); // 这里不要调用detectChanges,留到测试用例中手动触发 }); describe('ngOnInit', () => { it('should call function with Data', () => { const testData = [mockHolding(), mockCashAccount()]; // 先监听目标函数调用 const funcSpy = jest.spyOn(component, 'function').mockImplementation(() => {}); // 触发ngOnInit,完成组件的订阅逻辑 fixture.detectChanges(); // 手动发射测试数据 mockObservableSubject.next(testData); // 同步断言即可,不需要done回调 expect(funcSpy).toHaveBeenCalledWith(testData); }); });
方案2:直接使用冷Observable测试单次发射场景(写法更简洁)
如果你的observable$只会发射一次值,不需要后续动态发射,用这种写法更简单:
import { of } from 'rxjs'; let component: MyComponent; let fixture: ComponentFixture<MyComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [Modules], providers: [provideMock({})], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(MyComponent); component = fixture.componentInstance; // 这里不要提前给observable$赋值,也不要调用detectChanges }); describe('ngOnInit', () => { it('should call function with Data', () => { const testData = [mockHolding(), mockCashAccount()]; const funcSpy = jest.spyOn(component, 'function').mockImplementation(() => {}); // 先赋值测试用的冷Observable component['observable$'] = of(testData); // 触发ngOnInit执行订阅,of是同步的,订阅时就会直接发射值触发逻辑 fixture.detectChanges(); // 直接断言 expect(funcSpy).toHaveBeenCalledWith(testData); }); });
注意事项
- 所有对
observable$的赋值、对目标函数的spy操作都必须在触发ngOnInit之前完成,否则逻辑不会生效 - 不需要在测试用例中手动订阅
observable$,你要验证的是组件自身的订阅逻辑,只要让组件订阅的数据源发射值即可 of生成的冷Observable是同步执行的,不需要额外加done回调,也不需要用异步测试逻辑
内容的提问来源于stack exchange,提问作者Hyena
相关产品推荐
相关产品推荐

