TestBed

TestBed jest najważniejszym narzędziem do testowania aplikacji pisanych w Angular 2/4. Używając go tworzymy klasę typu @NgModule, którą później konfigurujemy za pomocą metody configureTestingModule otrzymując pełnoprawny moduł Angular'owy możliwy do testowania. Sama metoda configureTestingModule przyjmuje jako argumenty te same informacje jakich używamy w trakcie tworzenia modułu aplikacji - importy, deklaracje, schematy etc.

Na pierwszy ogień do testowania weźmy bardzo prosty komponent - HeaderComponent. Naszym celem w tej części będzie przetestowanie inicjalizacji modułu oraz czy tytuł zgadza się z oczekiwanym.

Testowany moduł ma następującą strukturę:

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

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

Jego szablon HTML wygląda następująco:

<md-toolbar color="primary">
  <span>Beers App</span>

  <a md-button [routerLink]="'/'">Start</a>
  <a md-button [routerLink]="'/random'">Random</a>
  <a md-button [routerLink]="'/favourite'">Favourite</a>
</md-toolbar>

Aby napisać zestaw testów do tego komponentu tworzymy w jego katalogu plik header.component.spec.ts i umieszczamy następującą treść:

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

import { HeaderComponent } from './header.component';

describe('HeaderComponent', () => {
  let component: HeaderComponent;
  let fixture: ComponentFixture<HeaderComponent>;
  let debugElement: DebugElement;
  let element: HTMLElement;

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

  beforeEach(async(() => {
    fixture = TestBed.createComponent(HeaderComponent);
    component = fixture.componentInstance;
    debugElement = fixture.debugElement.query(By.css('span'));
    element = debugElement.nativeElement;

    fixture.detectChanges();
  }));

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});

Przed uruchomieniem samego testu omówmy podstawowe rzeczy, które są w nim wykorzystane.

createComponent

Klasa TestBed posiada podstawową metodę - createComponent. Jej zadaniem jest utworzenie tzw. component test fixure. Jest to wskaźnik na środowisko testowe utworzonego komponentu. Daje ono dostęp zarówno do instancji samego komponentu jak i obiektu typu DebugElement.

debugElement

Property debugElement klasy ComponentFixture jest wskaźnikiem do elementu DOM testowanego komponentu. Dzięki metodzie query możemy za pomocą selektorów (na przykład) CSS otrzymać element HTML komponentu.

detectChanges

Fundamentalną częscią biblioteki Angular jest detekcja zmian w komponentach. Aby w teście powiedzieć bibliotece, że takowa zmiana nastąpiła wywołujemy metodę detectChanges co dokonuje wszelkich niezbędnych zmian w komponencie na wskutek zmian w nim.

Uruchomienie testu

Po uruchomieniu naszego testu otrzymamy komunikat:

  Can't bind to 'routerLink' since it isn't a known property of 'a'.

Stało się tak ponieważ w naszym module testowym nie zaimportowaliśmy modułu odpowiedzialnego za nawigację, który jest wykorzystany w szablonie komponentu (atrybut [routerLink]). Aby w środowisku testowym taki moduł zaimportować należy użyć klasy RouterTestingModule.

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { DebugElement } from '@angular/core';

import { HeaderComponent } from './header.component';

describe('HeaderComponent', () => {
  let component: HeaderComponent;
  let fixture: ComponentFixture<HeaderComponent>;
  let debugElement: DebugElement;
  let element: HTMLElement;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ HeaderComponent ],
      imports: [RouterTestingModule]
    }).compileComponents();
  }));

  beforeEach(async(() => {
    fixture = TestBed.createComponent(HeaderComponent);
    component = fixture.componentInstance;
    debugElement = fixture.debugElement.query(By.css('span'));
    element = debugElement.nativeElement;

    fixture.detectChanges();
  }));

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});

Po ponownym uruchomieniu testu otrzymamy inny komunikat o błędzie:

'md-toolbar' is not a known element

Stało się tak ponieważ procesor szablonów w Angularze nie wie czym jest atrybut md-toolbar. W naszym przypatku jest to właściwość CSS. Aby poinformować o tym Angulara w teście musimy dodatkowo ustawić schemat na CUSTOM_ELEMENTS_SCHEMA. Prawidłowy i pomyślnie przechodzący test wygląda następująco:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';

import { HeaderComponent } from './header.component';

describe('HeaderComponent', () => {
  let component: HeaderComponent;
  let fixture: ComponentFixture<HeaderComponent>;
  let debugElement: DebugElement;
  let element: HTMLElement;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ HeaderComponent ],
      imports: [RouterTestingModule],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    }).compileComponents();
  }));

  beforeEach(async(() => {
    fixture = TestBed.createComponent(HeaderComponent);
    component = fixture.componentInstance;
    debugElement = fixture.debugElement.query(By.css('span'));
    element = debugElement.nativeElement;

    fixture.detectChanges();
  }));

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});

Testowanie właściwości elementu

Aby przetestować czy nasz komponent nagłówka zawiera poprawny tytuł musimy dokonać sprawdzenia zawartości elementu span. Robimy to w następujący sposób:

  it('should have title', () => {
    expect(element.textContent).toContain('Beers App');
  });

Automatyczna detekcja zmian

Aby w naszych testach nie musieć za każdym razem wywoływać detekcji zmian możemy użyć specjalnego provider'a - ComponentFixtureAutoDetect. Jego konfiguracja wygląda następująco:

import { ComponentFixtureAutoDetect } from '@angular/core/testing';

beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [ HeaderComponent ],
    imports: [RouterTestingModule],
    schemas: [CUSTOM_ELEMENTS_SCHEMA],
    providers: [
      { provide: ComponentFixtureAutoDetect, useValue: true }
    ]
  }).compileComponents();
}));

results matching ""

    No results matching ""