Unit Testing/mocking Свойства окна в Angular2 (TypeScript)

Я строю некоторые модульные тесты для службы в Angular2.

В моей службе у меня есть следующий код:

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

Однако, когда я запускаю тест, содержащий этот код, он не работает.

Было бы здорово использовать все возможности Window, но поскольку я использую PhantomJs, я не думаю, что это возможно (я также пробовал Chrome, который дает те же результаты).

В AngularJs я бы прибегнул к издевательскому $Window (или, по крайней мере, к рассматриваемым свойствам), но поскольку для тестирования Angular2 не было много документации, я не уверен, как это сделать.

Может ли кто-нибудь помочь?

Ответ 1

В Angular 2 вы можете использовать функцию @Inject(), чтобы внедрить объект окна, назвав его с помощью строкового токена, например, так:

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

В @NgModule вы должны предоставить его, используя ту же строку:

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

Затем вы также можете высмеивать его, используя строку токена

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

Это работало в Angular 2.1.1, самое позднее на 2016-10-28.

Не работает с Angular 4.0.0 AOT.https://github.com/angular/angular/issues/15640

Ответ 2

Как упоминалось в комментарии @estus, вам лучше получить хэш от маршрутизатора. Но, чтобы ответить на ваш вопрос напрямую, вам нужно ввести окно в то место, где вы его используете, чтобы во время тестирования вы могли издеваться над ним.

Сначала зарегистрируйте окно с провайдером angular2 - возможно, где-то глобальным, если вы используете его повсеместно:

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

Это сообщает angular, когда инъекция зависимостей запрашивает тип Window, он должен вернуть глобальный Window.

Теперь, в том месте, где вы его используете, вы вводите это в свой класс, вместо того чтобы напрямую использовать глобальный:

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

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

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

Теперь вы готовы высмеять это значение в своем тесте.

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;
    //...
});

Ответ 3

После того, как RC4-метод provide(), его развращают, поэтому способ справиться с этим после RC4:

  let myMockWindow: Window;

  beforeEach(() => {
    myMockWindow = <any> { location: <any> {hash: 'WAOW-MOCK-HASH'}};
    addProviders([SomeService, {provide: Window, useValue: myMockWindow}]);
  });

Мне нужно время, чтобы понять, как это работает.