如何测试含Observable订阅且无需触发值发射的服务?
如何在单元测试中阻止Observable订阅回调执行?
问题场景
我有一个MyService服务,其构造函数中订阅了依赖AuthService的events Observable字段。编写单元测试时,我希望忽略该订阅——即在部分测试中不触发Observable发射值,因为要测试的是无关函数。但当前测试中,订阅的回调仍会执行,而我不想也无需MockrefreshToken方法。
相关代码
MyService
export class MyService { constructor(private authService: AuthService) { this.authService.configure(); this.authService.events.subscribe(async (event) => { await this.authService.refreshToken(); }); } }
AuthService(第三方依赖片段)
export declare class AuthService implements OnDestroy { events: Observable<Event>; // ... 其他字段/方法 }
当前测试代码
describe('MyService', () => { let authServiceSpy: jasmine.SpyObj<AuthService>; beforeEach(() => { authServiceSpy = jasmine.createSpyObj<AuthService>( ['configure', 'refreshToken'], { get events() { return defer(() => Promise.resolve(new Event('some_event'))); } } ); TestBed.configureTestingModule({}); }); it('should create service and configure authService', () => { const myService = new MyService(authServiceSpy); expect(myService).toBeTruthy(); expect(authServiceSpy.configure).toHaveBeenCalledOnce(); }); });
解决方案
核心是让authServiceSpy.events返回一个永远不会发射值的Observable,这样订阅后的回调逻辑就不会被触发。RxJS提供了两个最适合的工具:
EMPTY:立即完成但不发射任何值,订阅后直接进入完成阶段,不会执行next回调never():永远处于等待状态,既不发射值也不完成,同样不会触发回调
修改后的测试代码
把原来events的返回值替换成EMPTY或never()即可:
import { EMPTY, never } from 'rxjs'; // 记得导入RxJS操作符 describe('MyService', () => { let authServiceSpy: jasmine.SpyObj<AuthService>; beforeEach(() => { authServiceSpy = jasmine.createSpyObj<AuthService>( ['configure', 'refreshToken'], { // 方案1:用EMPTY,语义化表示“无值发射” get events() { return EMPTY; } // 方案2:用never(),永远不触发任何回调 // get events() { return never(); } } ); TestBed.configureTestingModule({}); }); it('should create service and configure authService', () => { const myService = new MyService(authServiceSpy); expect(myService).toBeTruthy(); expect(authServiceSpy.configure).toHaveBeenCalledOnce(); // 可以加这个断言验证refreshToken确实没被调用 expect(authServiceSpy.refreshToken).not.toHaveBeenCalled(); }); });
为什么原来的写法不行?
你之前用defer(() => Promise.resolve(new Event('some_event'))),会让Observable立即发射一个Event值,所以订阅的回调函数会被执行。换成EMPTY或never()后,Observable不会发射任何值,自然不会触发回调。
灵活扩展
如果后续某些测试用例需要让events发射值,只需在对应测试中覆盖authServiceSpy.events的返回值即可:
it('should handle auth event', () => { // 临时让events发射测试事件 authServiceSpy.events = of(new Event('test_event')); const myService = new MyService(authServiceSpy); // 这里可以编写对应逻辑的断言 });
内容的提问来源于stack exchange,提问作者Thelonias
相关产品推荐
相关产品推荐

