Angular下如何使用Jasmine测试LoginService构造函数的setInterval逻辑
问题根因
你在LoginService构造函数中启动了全局setInterval定时器,测试过程中实例化服务后这个定时器会一直挂在全局事件循环中。Jasmine默认会等待所有异步任务完成后才结束测试用例,而你设置的定时器需要30分钟才触发,远超过Jasmine默认的5秒超时阈值,因此会抛出超时错误。
解决方案
以下方案都不需要删除原有setInterval逻辑,同时还能优化生产环境的代码健壮性:
方案1:为服务添加销毁逻辑(推荐,兼顾生产环境内存泄漏问题)
首先修改LoginService,实现OnDestroy接口,在服务销毁时主动清除定时器:
@Injectable() export class LoginService implements OnDestroy { private intervalId: any; constructor(private readonly http: HttpClient, private readonly ngZone: NgZone) { this.ngZone.runOutsideAngular(() => { this.intervalId = setInterval(() => { ngZone.run(() => { this.renewJwtToken(); }); }, 1000 * 60 * 30); }); } callLogin(loginRequest: LoginRequest): Observable<HttpResponse<LoginResponse>> { // call in order to login user } renewJwtToken() { // call in order to renew the token } ngOnDestroy(): void { if (this.intervalId) { clearInterval(this.intervalId); } } }
之后在测试用例的afterEach钩子中销毁服务,清除残留定时器即可:
let service: LoginService; beforeEach(() => { TestBed.configureTestingModule({ // 原有测试配置 }); service = TestBed.inject(LoginService); }); afterEach(() => { service.ngOnDestroy(); TestBed.resetTestingModule(); });
方案2:使用Jasmine假定时器隔离真实异步逻辑
如果不想修改生产代码,可以直接在测试中使用Jasmine自带的假时钟能力,接管全局定时器逻辑,完全避免真实定时器的影响:
let service: LoginService; beforeEach(() => { // 安装假时钟,接管setTimeout、setInterval等全局定时方法 jasmine.clock().install(); TestBed.configureTestingModule({ // 原有测试配置 }); service = TestBed.inject(LoginService); }); afterEach(() => { // 卸载假时钟,恢复原生定时方法 jasmine.clock().uninstall(); });
如果需要验证token刷新逻辑,还可以通过jasmine.clock().tick(1000 * 60 * 30)快进30分钟,验证renewJwtToken方法是否被正常调用。
内容的提问来源于stack exchange,提问作者Gavi
相关产品推荐
相关产品推荐

