How to use Angular fake AsyncTest
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!
