Angular/Karma 单元测试错误“1 个计时器仍在队列中”
Posted
技术标签:
【中文标题】Angular/Karma 单元测试错误“1 个计时器仍在队列中”【英文标题】:Angular/Karma unit test error "1 timer(s) still in the queue" 【发布时间】:2020-02-13 10:49:10 【问题描述】:这不是我第一次遇到"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/);
));
));
我正在测试的是一个 Angular 组件,它是 Material 输入字段的包装器。该组件有许多可选属性,其中大多数只是用于常见输入字段功能的传递属性,但也有一些自定义属性,例如我在上面测试的用于大写/小写转换的属性。
case
属性的可接受值为upper
、lower
和mixed
(将空字符串、null 或未定义视为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.
...
谁能告诉我我可能做错了什么,或者是清除挥之不去的计时器的好方法?
免责声明:当我寻求 Karma 单元测试的帮助时,一个大问题是,即使我明确搜索“业力”,我也大多会找到 Pr0tractor、Pr0tractor 和更多 Pr0tractor 的答案。这不是 Pr0tractor! (故意拼错了一个零,所以它不会得到搜索匹配。)
更新:我可以像这样解决我的问题:
expect(() =>
component.inputComp.case = 'foo';
).toThrowError(/Invalid camp-input case attribute/);
这不如通过测试组件模板中的 HTML 属性分配(错误的)值来进行测试,因为我只是将值直接强制到属性本身的组件设置器中,但它'直到我有更好的解决方案。
【问题讨论】:
好的,除了测试问题,为什么不将属性定义为枚举或允许字符串的联合,这样可以在编译时捕获? 实际上,它是这样定义的 (type InputCase = 'lower' | 'mixed' | 'upper';
),但是通过 HTML 分配的属性并不能可靠地得到类型检查。
顺便问一下,您对我的免责声明有什么问题?
另外,通过使用检查有效性的设置器,该值也可以不区分大小写。
这是 Angular 的一个非常重要的功能(而不是错误功能),您可以创建自定义组件,然后让用户按照自己的意愿使用这些组件在 HTML 中,例如<my-input [(ngModel)]="partNumber" case="upper">
。至于引发异常与通过控制台警告悄悄失败,这两种方法都有优缺点,在这种情况下,无论如何这不是我的决定。
【参考方案1】:
我最近遇到了同样的问题 - 为了解决我在 it
函数结束时从 @angular/core/testing
调用了 discardPeriodicTasks()
并且在那之后我的测试通过了。
在这种情况下,您可能希望在最终的 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/);
));
tick
在你的 fakeAsync 上下文中移动时间。
flush
通过排空宏任务队列直到它为空来模拟该上下文中时间的完成。
discardPeriodicTasks
“抛出”任何剩余的周期性任务。
它们各自用于不同的目的,并且会有不同的用例。
【讨论】:
我已经从不久前遇到此问题的项目中移出,但我应该将此答案添加为书签,以便下次我重新进行更多 Angular 单元测试时尝试一下。 今天早上真的让我省了很多麻烦,所以我想分享一下。最佳 flush() 作为单元测试中的最后一条指令为我工作。不幸的是,discardPeriodicTasks() 没有效果:-)【参考方案2】:我也遇到过类似的问题。解决方案是flush函数用法。
import fakeAsync, flush from '@angular/core/testing';
it('test something', fakeAsync(() =>
// ...
flush();
));
【讨论】:
仅供参考:在处理按钮点击并要求考虑其副作用时,我偶然发现了 fakeAsync 的合法用例!在“异步”块等效项中使用 tick() 的要求是 setTimeout(()=>,0) 但与使用 fixture.whenStable() 一起使用更多的代码膨胀..以上是关于Angular/Karma 单元测试错误“1 个计时器仍在队列中”的主要内容,如果未能解决你的问题,请参考以下文章
Angular - Jasmine/karma - 订阅 lambda 表达式未执行
Angular Karma Jasmine 错误:非法状态:无法加载指令摘要
未捕获的类型错误:无法读取未定义抛出的属性“coSearchCriteria” - Angular Karma/Jasmine
Angular5 / Karma'选择器'不是已知元素[重复]