如何为Angular中使用RxJS bufferTime操作符的服务编写单元测试
问题根因
你的单元测试失败的核心原因是spy挂载的时机晚于服务构造函数执行时机:
beforeEach中调用TestBed.inject(AService)时,服务构造函数已经执行,setupObservables已经完成订阅绑定,此时绑定的是原生的doSomething$方法- 你后续在测试用例中才对实例的
doSomething$方法挂载spy,已经不会影响已经建立的订阅链路,所以即使时间到了方法被调用,spy也收不到通知 - 你手动二次调用
setupObservables会先取消旧订阅,再用已经被spy的方法重新建立订阅,所以才能断言通过
解决方案1:调整spy挂载时机(兼容原有fakeAsync写法)
在服务实例化之前,直接对类原型的方法挂载spy,这样构造函数执行时绑定的就是spy后的方法,不需要二次调用setupObservables:
import { discardPeriodicTasks, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { AService } from './service'; describe('AService', () => { let doSomethingSpy: jasmine.Spy; beforeEach(() => { // 实例化服务前先给原型方法挂spy doSomethingSpy = spyOn(AService.prototype, <any>'doSomething$').and.callThrough(); TestBed.configureTestingModule({}); }); fit('should trigger doSomething$ after 333ms when subject emits', fakeAsync(() => { const service = TestBed.inject(AService); service.nextSomeSubject(); tick(333); expect(doSomethingSpy).toHaveBeenCalled(); discardPeriodicTasks(); })); });
解决方案2:用RxJS TestScheduler测试时间操作符(更规范)
对于RxJS带时间逻辑的操作符,用官方提供的TestScheduler测试更精准,不会出现fakeAsync和RxJS调度器的兼容问题:
import { TestBed } from '@angular/core/testing'; import { AService } from './service'; import { TestScheduler } from 'rxjs/testing'; describe('AService', () => { let testScheduler: TestScheduler; let doSomethingSpy: jasmine.Spy; beforeEach(() => { testScheduler = new TestScheduler((actual, expected) => { expect(actual).toEqual(expected); }); doSomethingSpy = spyOn(AService.prototype, <any>'doSomething$').and.callThrough(); TestBed.configureTestingModule({}); }); fit('should trigger doSomething$ after 333ms when subject emits', () => { testScheduler.run(() => { const service = TestBed.inject(AService); service.nextSomeSubject(); // 手动推进333ms虚拟时间 testScheduler.elapseTime(333); expect(doSomethingSpy).toHaveBeenCalled(); }); }); });
内容的提问来源于stack exchange,提问作者jkyoutsey
相关产品推荐
相关产品推荐

