You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jasmine测试报队列仍有计时器错误 RxJS长按监听函数用例排查

报错原因
  • Jasmine 配套的 fakeAsync 工具会在测试运行时劫持全局的 setTimeout 方法,所有创建的定时器都会被记录到内部任务队列中。测试执行结束时如果队列中存在未执行的定时器,就会抛出Error: 1 timer(s) still in the queue.错误。
  • 第二个用例中,触发pointerdown后创建了500ms的定时器,仅tick(300)后就触发pointerup执行clearTimeout:原生的clearTimeout只会阻止定时器回调执行,不会将该定时器记录从fakeAsync的内部队列中移除,测试结束时队列中残留了剩余200ms的定时器记录,因此触发报错。
  • 额外隐患:测试结束后没有主动取消Observable订阅,导致finalize中的事件监听器清理逻辑不会执行,存在测试资源泄漏问题。
修复方案

只需要在第二个用例结束前显式清空fakeAsync的剩余定时器队列,同时主动取消订阅清理资源即可,修改后的测试用例代码如下:

it('2. should not emit if pressing on target for less than 500ms', fakeAsync(() => {
  const target = document.createElement('button');
  const longPressListenerSpy = jasmine.createSpy('longPressListenerSpy');
  // 存储订阅实例用于后续清理
  const subscription = watchForLongPress(target).subscribe({
    next: longPressListenerSpy
  });
  target.dispatchEvent(new Event('pointerdown'));
  tick(300);
  target.dispatchEvent(new Event('pointerup'));
  // 丢弃fakeAsync队列中所有剩余的定时器任务
  discardPeriodicTasks();
  // 主动取消订阅,触发finalize逻辑移除事件监听器
  subscription.unsubscribe();
  expect(longPressListenerSpy).toHaveBeenCalledTimes(0);
}));

如果不想调用discardPeriodicTasks(),也可以选择在触发pointerup后再tick(200)走完剩余的定时器时长,清空队列的效果完全一致。

可选优化(非必须)

如果要从根源避免这类定时器测试问题,可以将原函数的原生定时器替换为RxJS内置的timer操作符,配合RxJS测试调度器执行用例,完全对齐响应式编程范式,无需依赖fakeAsync的定时器劫持逻辑。

内容的提问来源于stack exchange,提问作者Trash Can

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 16:45:00