Enhetstesting/mocking av Window-egenskaper i Angular2 (TypeScript)


Jeg lager noen enhetstester for en tjeneste i Angular2.

Innenfor min tjeneste har jeg følgende kode:

var hash: string; hash=this.window.location.hash;

Men når jeg kjører en test som inneholder denne koden, vil den feile.

Det hadde vært flott å bruke alle funksjonene i Window, men siden jeg bruker PhantomJs, tror jeg ikke dette er mulig (jeg har også prøvd Chrome som gir de samme resultatene).

I AngularJs ville jeg ha tydd til å mocke $Window (eller i det minste de aktuelle egenskapene), men siden det ikke finnes mye dokumentasjon for enhetstesting i Angular2, er jeg ikke sikker på hvordan jeg skal gjøre dette.

Kan noen hjelpe?

Svar #1

I Angular 2 kan du bruke @Inject()-funksjonen for å injisere vindusobjektet ved å navngi det med en streng-token, slik:

  constructor( @Inject('Window') private window: Window) { }

I @NgModule må du deretter gi det ved å bruke den samme strengen:

@NgModule({
    declarations: [ ... ],
    imports: [ ... ],
    providers: [ { provide: 'Window', useValue: window } ],
})
export class AppModule {
}

Da kan du også gjøre narr av det ved å bruke token-strengen

beforeEach(() => {
  let windowMock: Window = <any>{ };
  TestBed.configureTestingModule({
    providers: [
      ApiUriService,
      { provide: 'Window', useFactory: (() => { return windowMock; }) }
    ]
  });

Dette fungerte i Angular 2.1.1, den nyeste per 2016-10-28.

Fungerer ikke med Angular 4.0.0 AOT. https://github.com/angular/angular/issues/15640

Svar #2

Som @estus nevnte i kommentaren, ville det være bedre å hente hashen fra ruteren. Men for å svare på spørsmålet ditt direkte, må du injisere window der du bruker det, slik at du kan mocke det under testing.

Først, registrer vinduet med angular2-leverandøren - sannsynligvis et sted globalt hvis du bruker dette overalt:

import { provide } from '@angular/core';
provide(Window, { useValue: window });

Dette forteller Angular at når avhengighetsinjeksjonen spør etter typen Window, bør den returnere den globale window.

Nå, på det stedet du bruker det, injiserer du dette i klassen din i stedet for å bruke globalen direkte:

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

@Component({ ... })
export default class MyCoolComponent {
    constructor (
        window: Window
    ) {}

    public myCoolFunction () {
        let hash: string;
        hash = this.window.location.hash;
    }
}

Nå er du klar til å gjøre narr av den verdien i testen din.

import {
    beforeEach,
    beforeEachProviders,
    describe,
    expect,
    it,
    inject,
    injectAsync
} from 'angular2/testing';

let myMockWindow: Window;
beforeEachProviders(() => [
    //Probably mock your thing a bit better than this..
    myMockWindow = <any> { location: <any> { hash: 'WAOW-MOCK-HASH' }};
    provide(Window, {useValue: myMockWindow})
]);

it('should do the things', () => {
    let mockHash = myMockWindow.location.hash;
    //...
});

Relaterte artikler