The problem with async is that we still have to introduce real waiting in our tests, and this can make our tests very slow.

One drawback of Async is that we still need to introduce real waits in our test code, which slows down our unit tests.

fakeAsync comes to the rescue and helps to test asynchronous code in a synchronous way.

FakeAsync allows you to test asynchronous code synchronously.

An example:

<h1>
  {{ incrementDecrement.value }}
</h1>

<button (click) ="increment()" class="increment">
  Increment
</button>
Copy the code

Implementation of increment method:

increment() {
  this.incrementDecrement.increment();
}
Copy the code

IncrementDecrement Service

increment() {
  setTimeout(() = > {
    if (this.value < 15) {
      this.value += 1;
      this.message = ' ';
    } else {
      this.message = 'Maximum reached! '; }},5000); // wait 5 seconds to increment the value
}
Copy the code

Test code:

import { TestBed, fakeAsync, tick, ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';

import { AppComponent } from './app.component';
import { IncrementDecrementService } from './increment-decrement.service';
describe('AppComponent'.() = > {
  let fixture: ComponentFixture<AppComponent>;
  let debugElement: DebugElement;
  beforeEach(
    async(() = > {
      TestBed.configureTestingModule({
        declarations: [AppComponent],
        providers: [IncrementDecrementService] }).compileComponents(); fixture = TestBed.createComponent(AppComponent); debugElement = fixture.debugElement; })); it('should increment in template after 5 seconds', fakeAsync(() = > {
      debugElement
        .query(By.css('button.increment'))
        .triggerEventHandler('click'.null);
  tick(2000);
  fixture.detectChanges();
  let value = debugElement.query(By.css('h1')).nativeElement.innerText;
  expect(value).toEqual('0'); // value should still be 0 after 2 seconds

  tick(3000);
  fixture.detectChanges();

  const value = debugElement.query(By.css('h1')).nativeElement.innerText;
  expect(value).toEqual('1'); // 3 seconds later, our value should now be 1
}));
Copy the code

FakeAsync block tick code that simulates the passage of time within a specific zone. The unit of the Tick parameter is milliseconds.

Tick can also be used with no argument, in which case it waits until all the microtasks are done (when promises are resolved for example).

If there is no parameter, go back until all Microtasks are finished, i.e. promises are Resolved.

Alternatively, you can use flush: It basically simulates the passage of time until the macrotask queue is empty. Macrotasks include things like setTimouts, setIntervals and requestAnimationFrame.

Flush is not returned until all microtasks have finished, including setTimeouts, setIntervals, and requestAnimationFrame.

An example of rewriting with flush:

it('should increment in template', fakeAsync(() = > {
  debugElement
    .query(By.css('button.increment'))
    .triggerEventHandler('click'.null);

  flush();
  fixture.detectChanges();

  const value = debugElement.query(By.css('h1')).nativeElement.innerText;
  expect(value).toEqual('1');
}));
Copy the code