我为Angular应用编写了此测试:
it('should request confirmation before deleting & abort action if user declined', fakeAsync(() => {
spyOn(appService, 'confirm').and.returnValue(of(false));
spyOn(personService, 'delete').and.callThrough();
component.deleteEntry(testPerson);
//tick(); // Missing tick()!
expect(personService.delete).not.toHaveBeenCalled();
}));
这是我正在测试的组件方法:
async deleteEntry(person: Person) {
if (await this.appService.confirm().toPromise()) {
return;
}
try {
await this.personService.delete(person).toPromise();
} catch(resp) {
this.appService.errorMsgBox();
}
}
({confirm()
的目的是显示一个确认对话框,并根据用户输入返回可观察到的发射true
/ false
)
如果仔细看,我的组件函数有错误。检查!
的结果时,我忘记了confirm()
运算符。正确的代码是
if (!await this.appService.confirm().toPromise()) {
但是,测试将通过。我不确定100%,但是我想它可以通过,因为最后的expect()
语句在confirm()
返回值之前执行检查。因此,是的,当然personService.delete()
还没有被调用。如果我取消对tick()
的注释,则测试将按预期工作并检测到错误。
现在,我希望fakeAsync()
由于待处理的微任务而引发错误。令我惊讶的是,事实并非如此。测试通过了,没有任何错误或警告,尽管the docs say:
如果函数结尾处有任何待处理的计时器,则会引发异常。
所以看来我们这里有一个竞争条件,即confirm()
在返回fakeAsync()
之前但在expect()
之后已解决。如果可能的话,fakeAsync()
如果不控制这些事情怎么办?
我和其他开发人员将来也可能会忘记tick()
或flushMicrotasks()
。所以我想知道如何避免这种情况。我缺少可以插入afterEach()
中的某种辅助函数吗?还是fakeAsync()
的行为是Angular错误,即它应该引发异常?
编辑
在Stackblitz上查看我的问题的完整工作示例:https://stackblitz.com/edit/angular-cnmubr。请注意,如果要重新运行测试或更改某些内容后,必须单击内部浏览器视图(在编辑器旁边)的“刷新”按钮。自动重新加载功能将无法正常工作并引发错误。
我提交了issue,就像评论中建议的那样。
答案 0 :(得分:0)
This issue显示,当前fakeAsync()
不会为未决的微任务抛出异常,而只会为未决的计时器抛出异常。目前,似乎没有办法确保测试结束时没有待处理的微任务。但是,开发人员将检查这是否是一项功能。
答案 1 :(得分:0)
如果您的测试至少以相同的方式编写,则如果tick
丢失/错误,则一个或另一个应失败。这不是完美的方法,但是您可以尝试将测试逻辑移到一个通用函数中,以便对它可以达到预期的目的有一定的信心:
function callDelete() {
spyOn(personService, 'delete').and.callThrough();
component.deleteEntry(testPerson);
//tick(); // Missing tick()!
}
it('aborts delete if user declined', fakeAsync(() => {
spyOn(appService, 'confirm').and.returnValue(of(false));
callDelete();
expect(personService.delete).not.toHaveBeenCalled();
}));
it('deletes if user accepted', fakeAsync(() => {
spyOn(appService, 'confirm').and.returnValue(of(true));
callDelete();
expect(personService.delete).toHaveBeenCalled();
}));
现在,您的第二项测试将失败(因为expect
在await
解决之前就已经运行了)。通过调试此错误,您将注意到被测代码中的逻辑错误,进行修复,然后找到测试错误。或者,您将找到并修复测试错误,这将导致第一个测试失败,然后找到逻辑错误。唯一会遗漏的方法是,如果tick
在一项测试中错了,但在另一项测试中错了(或者没有测试所有条件分支)。