How to use Angular fake AsyncTest


updated at 2018-12-20
AngularDay 20

WARNING: This is a Google translated (originally Chinese) and reposted article that probably is mostly comfortably understandable for senior JavaScript developers.

ZoneI'd like to write a new function around the original test, but since it has not been implemented yet, I fakeAsyncwill put together how to use it.
fakeAsyncHere is the official documentation, https://angular.io/guide/testing#async-test-with-fakeasync , some of which I recently updated (such as RxJS / Jasmine.clock), this article is a sample I would like to explain how to use it with code.

What is fakeAsync?

fakeAsyncIt is fakeAsyncTestZoneSpeca library that can test asynchronous operations (such as setTimeout) in sync using Angular .
For example: setTimeoutTo test : it will be like a scrape.
it('test setTimeout`, (done: DoneFn) => {
let a = 0;
setTimeout(() => a ++, 100);
setTimeout(() => {expect(a).toBe(1); done(); }, 100);
});
With such a way of writing, it takes time to test and it expectis troublesome when writing complicated tests . To fakeAsyncrewrite this case, it looks like the following.
it('test setTimeout with fakeAsync', fakeAsync(() => {
let a = 0;
setTimeout(() => a ++, 100);
tick(100);
expect(a).toBe(1);
}));
In this way, the asynchronous test was synchronized. delayWe can proceed without waiting for, expectand it became easier to write.

Asynchronous operation supporting fakeAsync

  • setTimeout
  • setInterval
  • Promise
  • setImmediate
  • requestAnimationFrame
Other Functions are zone.jsresponding steadily now .

fakeAsync Tips for actual use

  • Test component in cooperation with async / await For example component.spec.ts,
it('should show title correctly', () => {
component.title = 'hello';
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.nativeElement.querySelector(By.css('title')).textContent).toContain('hello');
});
});
If you async/await+fakeAsyncscratch this with , you will become easier to read.
// Utility function
async function runChangeDetection(fixture: ComponentFixture) {
fixture.detectChanges();
tick();
return await fixture.whenStable();
}

it('should show title correctly', async () => {
component.title = 'hello';
await runChangeDetection(fixture);
expect(fixture.nativeElement.querySelector(By.css('title')).textContent).toContain('hello');
});
  • Cooperate with Date.now
it('should get Date diff correctly in fakeAsync', fakeAsync(() => {
const start = Date.now();
tick(100);
const end = Date.now();
expect(end - start).toBe(100);
}));
  • Link with jasmine.clock
describe('use jasmine.clock()', () => {

beforeEach(() => { jasmine.clock().install(); });
afterEach(() => { jasmine.clock().uninstall(); });
it('should tick jasmine.clock with fakeAsync.tick', fakeAsync(() => {
// is in fakeAsync now, don't need to call fakeAsync(testFn)
let called = false;
setTimeout(() => { called = true; }, 100);
jasmine.clock().tick(100);
expect(called).toBe(true);
}));
});
Furthermore, jasmine.clockwhen you use, you can also enter fakeAsync for automation.
First src/test.tsof all , zone-testingbefore you import, add the oyster code,
(window as any)['__zone_symbol__fakeAsyncPatchLock'] = true;
import 'zone.js/dist/zone-testing';
Then, in the above case fakeAsync, the test case got into fakeAsync automatically as the call of.
describe('use jasmine.clock()', () => {
// need to config __zone_symbol__fakeAsyncPatchLock flag
// before loading zone.js/dist/zone-testing
beforeEach(() => { jasmine.clock().install(); });
afterEach(() => { jasmine.clock().uninstall(); });
it('should auto enter fakeAsync', () => {
// is in fakeAsync now, don't need to call fakeAsync(testFn)
let called = false;
setTimeout(() => { called = true; }, 100);
jasmine.clock().tick(100);
expect(called).toBe(true);
});
});
  • Cooperation with Rxjs Scheduler
With Rxjs there are Schedulers related to various times, delayetc, intervaland these can also fakeAsyncwork together.
First src/test.tsof all , zone-testingbefore you import, add the oyster code,
import 'zone.js/dist/zone-patch-rxjs-fake-async';
import 'zone.js/dist/zone-testing';
Then, the following rxjs scheduler case fakeAsynccan be executed with.
it('should get Date diff correctly in fakeAsync with rxjs scheduler', fakeAsync(() => {
// need to add `import 'zone.js/dist/zone-patch-rxjs-fake-async'
// to patch rxjs scheduler
let result = null;
of ('hello').pipe(delay(1000)).subscribe(v => { result = v; });
expect(result).toBeNull();
tick(1000);
expect(result).toBe('hello');

const start = new Date().getTime();
let dateDiff = 0;
interval(1000).pipe(take(2)).subscribe(() => dateDiff = (new Date().getTime() - start));

tick(1000);
expect(dateDiff).toBe(1000);
tick(1000);
expect(dateDiff).toBe(2000);
}));
  • Test of Interval
If you setIntervalcan control with test code, you need to clear the interval before the test is complete.
it('test setInterval', fakeAsync(() => {
let a = 0;
const intervalId = setInterval(() => a ++, 100);
tick(100);
expect(a).toBe(1);
tick(100);
expect(a).toBe(1);
// need to clearInterval, otherwise fakeAsync will throw error
clearInterval(intervalId);
}));
If setIntervalit is called in another function or library, discardPeriodicTasksyou need to call.
it('test interval lib', fakeAsync(() => {
let a = 0;
funcWithIntervalInside(() => a ++, 100);
tick(100);
expect(a).toBe(1);
tick(100);
expect(a).toBe(1);
// need to discardPeriodicTasks, otherwise fakeAsync will throw error
discardPeriodicTasks();
}));
  • When you want to execute all asynchronous operations of Pending now
For example, when testing a function, if you setTimeoutknow that there is a function in this function, but you delaydo not know the concrete thing flush, you can do it if you use.
it('test', fakeASync(() => {
someFuncWithTimeout();
flush();
}));
  • Now run Pending's Microtasks
Speaking of Microtasks, it will be the basics Promise.then. MacrotaskIf you do not want to run, Microtaskonly want to execute
flushMicrotasks, please use.
it('test', fakeASync(() => {
let a = 0;
let b = 0;
setTimeout(() => a ++);
Promise.resolve().then(() => {
b ++;
});
flushMicrotasks();
expect(a).toBe(0);
expect(b).toBe(1);
}));

from now on

Now zone.jsit fakeAsyncis undergoing renovation so that we can test various other asynchronous operations async/await + fakeAsync, it seems that Google's internal test case will be mostly heard from Google developers , I will do fakeAsyncmy best to expand more available cases from now on !
Thank you very much!