Angular fakeAsync test of promise resolution in ngOnInit()

Viewed 1578

For some reason, my fakeAsync tests will not resolve simple promises. I created a minimal example showing the issue (mostly ng-generated boilerplate).

My component under test contains a simple direct promise resolution in its ngOnInit method:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-simple-test',
  templateUrl: './simple-test.component.html',
  styleUrls: ['./simple-test.component.scss']
})
export class SimpleTestComponent implements OnInit {

  constructor() { }

  message: string;

  ngOnInit() {
    Promise.resolve('hello').then((content: string) => this.message = content);
  }

}

I am testing this promise with the following test:

import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';

import { SimpleTestComponent } from './simple-test.component';

describe('SimpleTestComponent', () => {
  let component: SimpleTestComponent;
  let fixture: ComponentFixture<SimpleTestComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ SimpleTestComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(SimpleTestComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should display "hello"', fakeAsync(() => {
    tick();
    expect(component.message).toBe('hello');
  }));
});

But the test fails, meaning the promise is not resolved at the time of expect, despite the forced promise resolution via tick().

It works when adding another explicit call to component.ngOnInit() at the beginning of the test. But this results in ngOnInit() being called twice. As far as I know, fixture.detectChanges() in beforeEach() should take care of ngOnInit() anyway.

What am I missing? Why isn't the promise resolved during tick()?

2 Answers

EDITED You need to call component.ngOnInit() first, then tick(), then your expect method. Do this in each test method, that is what is working for my tests.

code snippet

  beforeEach(() => {
    fixture = TestBed.createComponent(SimpleTestComponent);
    component = fixture.componentInstance;
  });

  it('should display "hello"', fakeAsync(() => {
    component.ngOnInit();
    tick();
    expect(component.message).toBe('hello');
  }));

Found the issue. ng g component generates the test with fixture.detectChanges() in the beforeEach(...) function which is outside the fakeAsync zone, so the promise can't be resolved by tick().

Moving fixture.detectChanges() into the fakeAsync zone fixes it for me:

  beforeEach(() => {
    fixture = TestBed.createComponent(SimpleTestComponent);
    component = fixture.componentInstance;
  });

  it('should display "hello"', fakeAsync(() => {
    fixture.detectChanges();
    tick();
    expect(component.message).toBe('hello');
  }));
Related