Angular 2 fakeAsync 在使用tick()的函数中等待超时?

我正在尝试从 Angular 2 中的模拟后端获取结果以进行单元测试.目前,我们正在使用带有超时的 fakeAsync 来模拟时间的流逝.

I'm trying to get the results from a mock backend in Angular 2 for unit testing. Currently, we are using fakeAsync with a timeout to simulate the passing of time.

当前工作单元测试

it('timeout (fakeAsync/tick)', fakeAsync(() => {
    counter.getTimeout();
    tick(3000); //manually specify the waiting time
}));

但是,这意味着我们仅限于手动定义的超时.不是在异步任务完成时.我想做的是让 tick() 等到任务完成后再继续测试.

But, this means that we are limited to a manually defined timeout. Not when the async task is completed. What I'm trying to do is getting tick() to wait until the task is completed before continuing with the test.

这似乎没有按预期工作.

This does not seem to work as intended.

阅读 fakeAsynctick 答案这里 解释说:

Reading up on the fakeAsync and tick the answer here explains that:

tick() 模拟时间的异步流逝.

tick() simulates the asynchronous passage of time.

我设置了一个 plnkr 示例来模拟这种情况.

I set up a plnkr example simulating this scenario.

在这里,我们调用 getTimeout() 方法,该方法调用具有超时的内部异步任务.在测试中,我们尝试包装它并在调用 getTimeout() 方法后调用 tick().

Here, we call the getTimeout() method which calls an internal async task that has a timeout. In the test, we try wrapping it and calling tick() after calling the getTimeout() method.

counter.ts

getTimeout() {
  setTimeout(() => {
    console.log('timeout')
  },3000)
}

counter.specs.ts

it('timeout (fakeAsync/tick)', fakeAsync(() => {
    counter.getTimeout();
    tick();
}));

但是,单元测试失败并出现错误错误:1 个计时器仍在队列中."

But, the unit test fails with the error "Error: 1 timer(s) still in the queue."

角度回购中的问题是否与此有关?

Does the issue here in the angular repo have anything to do with this?

是否可以这样使用tick()来等待超时函数?还是我可以使用另一种方法?

Is it possible to use tick() this way to wait for a timeout function? Or is there another approach that I can use?

推荐答案

fakeAsync 的目的是在你的规范内控制时间.tick 不会等待任何时间,因为它是用于模拟时间流逝的同步函数.如果您想等到异步功能完成,您将需要使用 asyncwhenStable,但是,在您的示例中,规范需要 3 秒才能完成通过,所以我不建议这样做.

The purpose of fakeAsync is to control time within your spec. tick will not wait for any time as it is a synchronous function used to simulate the passage of time. If you want to wait until the asynchronous function is complete, you are going to need to use async and whenStable, however, in your example, the spec will take 3 seconds to pass so I wouldn't advise this.

counter.spec.ts 失败的原因是您只模拟了 0 秒的过去(通常用于表示事件循环的下一个滴答声).因此,当规范完成时,仍然有模拟的计时器处于活动状态,这使整个规范失败.通过通知您超时已被模拟并且未处理,它实际上可以正常工作.

The reason why the counter.spec.ts is failing is that you have only simulated the passage of 0 seconds (typically used to represent the next tick of the event loop). So when the spec completes, there are still mocked timers active and that fails the whole spec. It is actually working properly by informing you that a timeout has been mocked an is unhandled.

基本上,我认为您正在尝试以不打算使用它们的方式使用 fakeAsynctick.如果您需要按照您建议的方式测试超时,最简单的方法是自己模拟 setTimeout 函数,这样,无论使用多少时间,您都可以调用该方法.

Basically, I think you are attempting to use fakeAsync and tick in ways for which they were not intended to be used. If you need to test a timeout the way that you have proposed, the simplest way would be to mock the setTimeout function yourself so that, regardless of the time used, you can just call the method.

已编辑我遇到了一个相关的问题,我想清除计时器,因为它不是被测试的部分,所以我不在乎花了多长时间.我试过了:

EDITED I ran into a related issue where I wanted to clear the timers, and since it was not the part under test, I didn't care how long it took. I tried:

tick(Infinity);

这很有效,但是超级hacky.我最终选择了

Which worked, but was super hacky. I ended up going with

discardPeriodicTasks();

我所有的计时器都被清除了.

And all of my timers were cleared.

相关文章