Life cycle methods not invoked in unit tests

I’m following this unit testing example, referenced on the Ionic blog and I’ve found that the Ionic lifecycle methods (eg. ionViewDidLoad) do not get called when executing these tests. I modified page1 to set some text in ionViewDidLoad and then display that text on the page - a common pattern I use in my app and would like to test. However, the test fails because ionViewDidLoad does not get called. I was expecting either createComponent or detectChanges to invoke the Ionic lifecycle. Am I missing something?

page1.ts

import { Component } from '@angular/core';
import { IonicPage, NavController } from 'ionic-angular';

@IonicPage()
@Component({
  selector: 'page-page1',
  templateUrl: 'page1.html'
})
export class Page1 {
  title: string = 'Ionic did not load';

  constructor(public navCtrl: NavController) {
  }

  ionViewDidLoad(): void {
    this.title = 'Ionic loaded...';
  }

}

page1.html

<ion-header>
  <ion-navbar>
    <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title>Page One</ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <h3>{{ title }}</h3>

  <img src="assets/icon/favicon.ico" alt="" />

  <p>
    If you get lost, the <a href="http://ionicframework.com/docs/v2">docs</a> will show you the way.
  </p>

  <button ion-button secondary menuToggle>Toggle Menu</button>
</ion-content>

page1.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By }           from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { Page1 } from './page1';
import { IonicModule, Platform, NavController} from 'ionic-angular/index';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';

describe('Page1', () => {
  let de: DebugElement;
  let comp: Page1;
  let fixture: ComponentFixture<Page1>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [Page1],
      imports: [
        IonicModule.forRoot(Page1)
      ],
      providers: [
        NavController
      ]
    });
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(Page1);
    comp = fixture.componentInstance;
    de = fixture.debugElement.query(By.css('h3'));
  });

  it('should create component', () => expect(comp).toBeDefined());

  it('should have expected <h3> text', () => {
    fixture.detectChanges();
    const h3 = de.nativeElement;
    expect(h3.innerText).toMatch('Ionic loaded...',
      '<h3> should say something about "Ionic"');
  });

  it('should show the favicon as <img>', () => {
    fixture.detectChanges();
    const img: HTMLImageElement = fixture.debugElement.query(By.css('img')).nativeElement;
    expect(img.src).toContain('assets/icon/favicon.ico');
  });
});

output:

Error: Expected ‘Ionic did not load’ to match ‘Ionic loaded…’, ’

should say something about “Ionic”’.

1 Like

First, in my opinion with unit testing, you should be testing your code, and not what is being displayed. Testing elements in the DOM is usually done with end-to-end testing.

Second, when you test results of lifecycle methods, you actually need to call them. I know, seems weird, but there you are. This is how I’d test it:

it('should have correct title', () => {
    expect(component.title).toEqual('Ionic did not load');
    component.ionViewDidLoad();
    fixture.detectChanges();
    expect(component.title).toEqual('Ionic loaded...');
  });

I always have this link open when I am working on unit tests: https://angular.io/guide/testing

Hope this helps.

That makes sense. I will look into end-to-end testing. Thanks for the info!