如何测试Angular中用户闲置15分钟后自动登出的函数?
问题描述
我正在使用Angular/Jasmine/Karma测试一个在ngOnInit中运行的函数,该函数会在用户15分钟无操作后执行登出。
原组件中相关函数代码:
#setSessionTimeout() { const clicks$ = fromEvent(document, 'click'); const checkInterval = 60000; // 1 minute const timeLimit = 15; // 15 minutes clicks$.pipe( startWith('fake click'), switchMap(() => interval(checkInterval).pipe( tap(value => { console.log(value); if (value > timeLimit) { this.router.navigate(['logout']); } }) ) ) ).subscribe() }
我的测试代码:
fit('should log out a user after 15 minutes of inactivity', fakeAsync(() => { tick(1000 * 60 * 60 * 60); fixture.detectChanges(); expect(routerMock.navigate).toHaveBeenCalledWith(['logout']); }));
目前测试无法通过,且函数似乎未执行(看不到console.log输出),请问该如何正确测试这个函数?
解决步骤
先触发组件初始化逻辑
测试中必须先调用fixture.detectChanges()触发ngOnInit,否则setSessionTimeout函数根本不会执行,自然看不到日志和登出逻辑。修正
tick的时间参数
原代码里interval每分钟触发一次,value从0开始递增:- 第1次触发(1分钟后):
value=1 - ...
- 第15次触发(15分钟后):
value=15,此时不满足value > timeLimit - 第16次触发(16分钟后):
value=16,才会触发登出
所以需要tick(16 * 60 * 1000),而不是你写的1小时。
- 第1次触发(1分钟后):
正确的测试代码示例
fit('should log out a user after 15 minutes of inactivity', fakeAsync(() => { // 触发组件初始化,执行ngOnInit和setSessionTimeout fixture.detectChanges(); // 模拟等待16分钟,让interval触发到value=16 tick(16 * 60 * 1000); // 触发变更检测,确保router.navigate的调用被捕获 fixture.detectChanges(); expect(routerMock.navigate).toHaveBeenCalledWith(['logout']); }));
- 可选:验证点击重置计时器的逻辑
如果需要覆盖完整场景,可以模拟用户点击,验证计时器会重置:
fit('should reset timeout on user click', fakeAsync(() => { fixture.detectChanges(); // 等待10分钟 tick(10 * 60 * 1000); // 模拟用户点击document,触发计时器重置 document.dispatchEvent(new Event('click')); // 再等待10分钟,此时总时长20分钟但因重置不会登出 tick(10 * 60 * 1000); expect(routerMock.navigate).not.toHaveBeenCalled(); // 再等待6分钟,累计16分钟无操作,触发登出 tick(6 * 60 * 1000); expect(routerMock.navigate).toHaveBeenCalledWith(['logout']); }));
- 清理订阅避免内存泄漏
建议在组件中保存setSessionTimeout的订阅对象,在ngOnDestroy中取消订阅,测试时也可以在afterEach里手动清理,避免影响其他测试用例:
// 组件内修改 private timeoutSubscription!: Subscription; #setSessionTimeout() { // ... 原有代码 ... this.timeoutSubscription = clicks$.pipe(...).subscribe(); } ngOnDestroy() { this.timeoutSubscription?.unsubscribe(); }
内容的提问来源于stack exchange,提问作者dman
相关产品推荐
相关产品推荐

