Angular/Karma 单元测试错误“1 个计时器仍在队列中”

ksh*_*ine 28 unit-testing typescript karma-jasmine angular

这不是我第一次遇到"1 timer(s) still in the queue",但通常我会找到某种方法使用tick()detectChanges()等来摆脱它。

下面的测试工作正常,直到我尝试测试我知道应该抛出异常的条件:

  it('should be able to change case', fakeAsync(() => {
    expect(component).toBeTruthy();

    fixture.whenStable().then(fakeAsync(() => {
      component.case = 'lower';
      fixture.autoDetectChanges();
      tick(500);
      const input = fixture.nativeElement.querySelector('input') as HTMLInputElement;
      typeInElement('abcDEF', input);
      fixture.autoDetectChanges();
      tick(500);
      expect(component.text).toEqual('abcdef');

      component.case = 'upper';
      fixture.autoDetectChanges();
      tick(500);
      typeInElement('abcDEF', input);
      fixture.autoDetectChanges();
      tick(500);
      expect(component.text).toEqual('ABCDEF');

      // Everything above works fine. Here's where the trouble begins
      expect(() => {
        component.case = 'foo';
        fixture.autoDetectChanges();
        tick(500);
      }).toThrowError(/Invalid case attribute/);
    }));
  }));
Run Code Online (Sandbox Code Playgroud)

我正在测试的是一个 Angular 组件,它是 Material 输入字段的包装器。该组件有许多可选属性,其中大多数只是常见输入字段功能的传递属性,但也有一些自定义属性,例如我在上面测试的用于大写/小写转换的属性。

为可接受的值case属性是upperlowermixed(与空字符串,空或未定义视为mixed)。该组件应该为其他任何事情抛出异常。显然它确实如此,并且测试成功了,但是随着我获得的成功:

ERROR: 'Unhandled Promise rejection:', '1 timer(s) still in the queue.', '; Zone:', 'ProxyZone', '; Task:', 'Promise.then', '; Value:', Error: 1 timer(s) still in the queue.
Error: 1 timer(s) still in the queue.
   ...
Run Code Online (Sandbox Code Playgroud)

谁能告诉我我可能做错了什么,或者是清除挥之不去的计时器的好方法?

免责声明:当我寻求 Karma 单元测试帮助时的一个大问题是,即使我明确搜索“karma”,我也主要找到 Pr0tractor、Pr0tractor 和更多 Pr0tractor 的答案。这不是 Pr0tractor!(故意用零拼错,所以它不会得到搜索匹配。)

更新:我可以像这样解决我的问题:

      expect(() => {
        component.inputComp.case = 'foo';
      }).toThrowError(/Invalid camp-input case attribute/);
Run Code Online (Sandbox Code Playgroud)

这不像通过测试组件模板中的 HTML 属性分配(坏)值那么好,因为我只是将值直接强加到组件的属性本身的 setter 中,但它会一直执行到我有一个更好的解决方案。

Ste*_*rnK 48

我遇到过类似的问题。解决方案是使用冲洗功能。

import { fakeAsync, flush } from '@angular/core/testing';

it('test something', fakeAsync(() => {

  // ...

  flush();
}));
Run Code Online (Sandbox Code Playgroud)


Boo*_*ore 40

我遇到同样的问题,最近-解决我叫discardPeriodicTasks()-从@angular/core/testing在我结束it功能和我的测试后通过。

在这种情况下,您可能希望在最终结果之前插入它 expect

 it('should be able to change case', fakeAsync(() => {
    expect(component).toBeTruthy();

    fixture.whenStable().then(fakeAsync(() => {
      component.case = 'lower';
      fixture.autoDetectChanges();
      tick(500);
      const input = fixture.nativeElement.querySelector('input') as HTMLInputElement;
      typeInElement('abcDEF', input);
      fixture.autoDetectChanges();
      tick(500);
      expect(component.text).toEqual('abcdef');

      component.case = 'upper';
      fixture.autoDetectChanges();
      tick(500);
      typeInElement('abcDEF', input);
      fixture.autoDetectChanges();
      tick(500);
      expect(component.text).toEqual('ABCDEF');

      discardPeriodicTasks() <-------------------- try here

      // Everything above works fine. Here's where the trouble begins
      expect(() => {
        component.case = 'foo';
        fixture.autoDetectChanges();
        tick(500);
      }).toThrowError(/Invalid case attribute/);
      
    }));
Run Code Online (Sandbox Code Playgroud)

tick 用于在您的 fakeAsync 上下文中向前移动时间。

flush 通过排空宏任务队列直到它为空来模拟该上下文中时间的完成。

discardPeriodicTasks “抛出”任何剩余的周期性任务。

它们各自用于不同的目的,并且具有不同的用例。

  • 今天早上这确实让我很头疼,所以我想分享一下。最好的 (2认同)

Nag*_*yan 9

您可以在测试用例的最后一行中使用discardPeriodicTasks()方法来消除剩余的“计时器仍在队列中”问题