在tick / flushMicroservice之后,fakeAsync是否保证承诺完成(Does fakeAsync guarantee promise completion after tick/flushMicroservice)

主要问题是如何测试Promise完成后执行的预期操作,例如测试组件是否在接收到某些远程内容后正确更新其状态。

在下面的规范中, dealWithIt()模拟为响应完成的promise而执行的逻辑(它更新变量并触发“另一个异步事件”)。

it('Promises fulfilled by flushMicrotasks',fakeAsync((): void => { let x = 1; let y = 2; let dealWithIt = function(p:Promise<number>) { p.then( v => { x = v; Promise.resolve(v).then( v=> {y = v+1; }); }); }; let p = Promise.resolve(y); dealWithIt(p); flushMicrotasks(); //valid if promise handling completed expect(x).toBe(2); expect(y).toBe(3); })); it('Promises fulfilled by tick',fakeAsync((): void => { let x = 1; let y = 2; let dealWithIt = function(p:Promise<number>) { p.then( v => { x = v; Promise.resolve(v).then( v=> {y = v+1; }); }); }; let p = Promise.resolve(y); dealWithIt(p); tick(); //valid if promise handling completed expect(x).toBe(2); expect(y).toBe(3); }));

两个测试都通过了。 在检查期望时,承诺得到了正确处理。

但是, 它有保证吗? 或者我只是幸运。

是否有任何限制,将在fakeAsync范围内创建的所有Promise通过调用tick或flushMicrotasks来“解雇”。

Main issue is how to test that expected actions were performed once a Promise was completed, for example to test if a component correctly updates its states after receiving some remote content.

In the specs below, the dealWithIt() simulates a logic performed in response to a completed promise (it updates variable and trigger "another async event").

it('Promises fulfilled by flushMicrotasks',fakeAsync((): void => { let x = 1; let y = 2; let dealWithIt = function(p:Promise<number>) { p.then( v => { x = v; Promise.resolve(v).then( v=> {y = v+1; }); }); }; let p = Promise.resolve(y); dealWithIt(p); flushMicrotasks(); //valid if promise handling completed expect(x).toBe(2); expect(y).toBe(3); })); it('Promises fulfilled by tick',fakeAsync((): void => { let x = 1; let y = 2; let dealWithIt = function(p:Promise<number>) { p.then( v => { x = v; Promise.resolve(v).then( v=> {y = v+1; }); }); }; let p = Promise.resolve(y); dealWithIt(p); tick(); //valid if promise handling completed expect(x).toBe(2); expect(y).toBe(3); }));

And both tests pass. At the time of checking expectations the promise was dealt with correctly.

But, is it guaranteed? Or was I just lucky.

Are there any limitations, will all Promises created in the scope of fakeAsync be 'fired' by calling tick or flushMicrotasks.

最满意答案

一起使用fakeAsync和tick / flushMicrotasks允许您以“同步”方式模拟异步处理。 因此,保证您在then方法中指定的回调在执行期望之前执行。

从文档:

fakeAsync

包装一个函数以在fakeAsync区域中执行:

通过调用flushMicrotasks()手动执行微任务, 定时器是同步的,tick()模拟异步时间流逝。

如果函数末尾有任何挂起的计时器,则会抛出异常。

export tick(millis ?: number):void

从angular2 / src / testing / fake_async.ts中定义的angular2 / testing导出(第84行)模拟fakeAsync区域中定时器的异步时间流逝。

在任何定时器回调已经执行之后,microtasks队列在这个函数开始时就被耗尽了。

flushMicrotasks

导出flushMicrotasks():void

刷新任何待处理的微任务。

这是相应的plunkr:

https://plnkr.co/edit/ARGGaY?p=preview

在引擎盖下

实际上, fakeAsync函数创建了一个专用区域,用于拦截函数setTimeout , clearTimeout , setInterval , clearInterval异步处理,但也会覆盖scheduleMicrotask函数。 这样fakeAsync可以完全控制异步处理并模拟异步处理。 它依赖于Jasmine的DelayedFunctionScheduler 。

通过使用then方法在promise上注册回调,通过调用之前在自定义区域中定义的scheduleMicrotask函数,立即排队微任务。 通过这种方式,该区域可以在何时执行承诺回调。

flushMicrotasks函数只是遍历已注册的微任务并同步执行它们(包括之前注册的promise的回调函数)。 tick执行相同的操作,但另外调用Jasmine调度程序的tick方法。

本文可以提供更多详细信息:

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

你可以看看fakeAsync的源代码:

https://github.com/angular/angular/blob/master/modules/angular2/src/testing/fake_async.ts#L34

Using together fakeAsync and tick / flushMicrotasks allows you to simulate asynchronous processing but in a "synchronous" way. So it's guaranteed that the callback you specified in your then method is executed before executed your expectations.

From the documentation:

fakeAsync

Wraps a function to be executed in the fakeAsync zone:

microtasks are manually executed by calling flushMicrotasks(), timers are synchronous, tick() simulates the asynchronous passage of time.

If there are any pending timers at the end of the function, an exception will be thrown.

tick

export tick(millis?: number) : void

exported from angular2/testing defined in angular2/src/testing/fake_async.ts (line 84) Simulates the asynchronous passage of time for the timers in the fakeAsync zone.

The microtasks queue is drained at the very start of this function and after any timer callback has been executed.

flushMicrotasks

export flushMicrotasks() : void

Flush any pending microtasks.

Here is the corresponding plunkr:

https://plnkr.co/edit/ARGGaY?p=preview

Under the hood

In fact the fakeAsync function creates a dedicated zone that intercepts asynchronous processing of functions setTimeout, clearTimeout, setInterval, clearInterval but also override the scheduleMicrotask function. This way the fakeAsync can completely control asynchronous processing and simulate asynchronous processing. It relies on the DelayedFunctionScheduler from Jasmine.

Registering a callback on a promise using the then method immediately queues a microtask by calling the scheduleMicrotask function previously defined in the custom zone. This way this zone has the hand on when to execute the promise callback.

The flushMicrotasks function simply iterates over the registered microtasks and execute them synchronously (included the previously registered callback of the promise). The tick does the same but calls in addition the tick method of the Jasmine scheduler.

This article could give more details:

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

You could have a look at the source code of the fakeAsync:

https://github.com/angular/angular/blob/master/modules/angular2/src/testing/fake_async.ts#L34

更多推荐