Angular
  • Angular learning
  • Angular
    • Change Detection
      • Angular Change Detection Strategies
      • Understanding Change Detection Strategy in Angular
    • Angular Components Overview
      • Lifecycle hooks
      • View encapsulation
    • Text interpolation
    • Pipes
    • ARIA
    • Event binding
    • Directives
    • Dependency injection in Angular
    • Difference between Template-Driven and Reactive Forms
    • Guards
    • Resolvers
      • Resolver example
  • Memory management in Angular applications
  • Renderer2
  • Angular test
    • Testing
      • The different types of tests
      • Some testing best practices
      • Angular Service Testing in Depth
        • About Jasmine
        • Jasmine - Test suites
        • Implementation of First Jasmine Specfication
        • spyOn() & jasmine.createSpyObj()
        • beforeEach()
        • Testing services
        • Disabled and Focused Tests
        • flush
        • HttpTestingController
        • Sample code
      • Angular Component Testing in Depth
        • Intro to Angular Component testing
        • DOM interaction
        • Trigger Change Detection
        • Test Card List Test Suite Conclusion
        • Window.requestAnimationFrame()
        • Asynchronous Work (Jasmine)
        • Cooperative asynchronous JavaScript: Timeouts and intervals
        • FakeAsync - Asynchronous Work (Jasmine) part 2
        • tick()
        • Sample codes
      • Testing Promised-based code-intro Microtasks
        • Microtasks
        • Micro-tasks within an event loop (Summary)
        • Macro-tasks within an event loop (Summary)
        • Test promised Microtasks (code)
      • Using fakeAsync to test Async Observables
      • Cypress.io
        • Create our first e2e test
      • Angular CLI code coverage and deployment in prod mode.
      • Travis CI
  • Angular best practices
    • Angular best practices
      • Security
      • Accessibility in Angular
      • Keeping your Angular projects up-to-date
    • Bootstrapping an Angular Application
      • Understanding the File Structure
      • Bootstrapping Providers
    • Components in Angular
      • Creating Components
      • Application Structure with Components
        • Accessing Child Components from Template
        • Using Two-Way Data Binding
        • Responding to Component Events
        • Passing Data into a Component
      • Projection
      • Structuring Applications with Components
      • Using Other Components
  • Reactive extensions
    • RxJS
      • RxJS Operators
      • of
      • Observable
      • async pipe (Angular)
      • Interval
      • fromEvent
      • Pipe
      • Map
      • Tap
      • ShareReplay
      • Concat
      • ConcatMap
      • Merge
      • MergeMap
      • ExhaustMap
      • fromEvent
      • DebounceTime
        • Type Ahead
      • Distinct Until Changed
      • SwitchMap
      • CatchError
      • Finalize
      • RetryWhen
      • DelayWhen
      • ForkJoin
      • First
      • Interview Questions
      • Zip
  • NgRx
    • What's NgRx
      • Actions
      • Reducers
      • Selectors
      • 🙅‍♂️Authentication guard with NgRX
      • @ngrx/effects
        • Side-Effect refresh survivor
  • Interview Q&A
    • Angular Unit Testing Interview Questions
    • Angular Questions And Answers
  • Angular Advanced
    • Setting up our environment
      • Understanding Accessors (TS)
      • The host & ::ng-deep Pseudo Selector
Powered by GitBook
On this page
  • FakeAsync
  • So, using flush, we can write a test like this for example:

Was this helpful?

  1. Angular test
  2. Testing
  3. Angular Component Testing in Depth

FakeAsync - Asynchronous Work (Jasmine) part 2

FakeAsync

The problem with async is that we still have to introduce real waiting in our tests, and this can make our tests very slow. fakeAsync comes to the rescue and helps to test asynchronous code in a synchronous way. To demonstrate fakeAsync, let’s start with a simple example. Say our component template has a button that increments a value like this:

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

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

It calls an increment method in the component class that looks like this:

app.component.ts

increment() {
  this.incrementDecrement.increment();
}

And this method itself calls a method in an incrementDecrement service that has an increment method that’s made asynchronous with the use of a setTimeout:increment-decrement.service.ts

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

Obviously, in a real-world app, this asynchronicity can be introduced in a number of different ways.

Let’s now use fakeAsync with the tick utility to run an integration test and make sure the value is incremented in the template:app.component.spec.ts

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);
  <span class="code-annotation">tick(2000);</span>
  fixture.detectChanges();
  let value = debugElement.query(By.css('h1')).nativeElement.innerText;
  expect(value).toEqual('0'); // value should still be 0 after 2 seconds

  <span class="code-annotation">tick(3000)</span>;
  fixture.detectChanges();

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

Notice how the tick utility is used inside a fakeAsync block to simulate the passage of time. The argument passed-in to tick is the number of milliseconds to pass, and these are cumulative within a test.

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).

Specifying the passing time like that can quickly become cumbersome, and can become a problem when you don’t know how much time should pass. A new utility called flush was introduced in Angular 4.2 and helps with that issue. It basically simulates the passage of time until the macrotask queue is empty. Macrotasks include things like setTimouts, setIntervals and requestAnimationFrame.

So, using flush, we can write a test like this for example:

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');
}));

Or it can also be like this:

it('Async test example - setTimeout() with flush()', fakeAsync(() => {
    let test = false;
    setTimeout(() => {});
    setTimeout(() => {
        test = true;
        expect(test).toBeTruthy();
    }, 1000);
    flush();
}));
PreviousCooperative asynchronous JavaScript: Timeouts and intervalsNexttick()

Last updated 4 years ago

Was this helpful?