簡體   English   中英

Angular2 (TypeScript) 中的單元測試/模擬窗口屬性

[英]Unit Testing/mocking Window properties in Angular2 (TypeScript)

我正在為 Angular2 中的服務構建一些單元測試。

在我的服務中,我有以下代碼:

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

但是,當我運行包含此代碼的測試時,它將失敗。

使用 Window 的所有功能會很棒,但是當我使用 PhantomJs 時,我認為這是不可能的(我也嘗試過 Chrome,它產生了相同的結果)。

在 AngularJs 中,我會求助於模擬 $Window (或至少是有問題的屬性),但由於 Angular2 單元測試的文檔不多,我不知道如何做到這一點。

任何人都可以幫忙嗎?

在 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

正如@estus 在評論中提到的,您最好從路由器獲取哈希值。 但是要直接回答您的問題,您需要將 window 注入您使用它的地方,以便在測試期間您可以模擬它。

首先,向 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;
    //...
});

在 RC4 方法provide()它被棄用了,所以在 RC4 之后處理這個的方法是:

  let myMockWindow: Window;

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

我需要一段時間才能弄清楚它是如何工作的。

我真的不明白為什么沒有提供這是由角團隊推薦方式來測試服務,你可以看到最簡單的解決方案在這里 在大多數情況下,您甚至根本不需要處理 TestBed 的東西。

此外,您也可以將這種方法用於組件和指令。 在這種情況下,您不會創建組件實例,而是創建類實例。 這意味着,您也不必處理組件模板中使用的子組件。

假設您能夠將 Window 注入到構造函數中

constructor(@Inject(WINDOW_TOKEN) private _window: Window) {}

只需在您的 .spec 文件中執行以下操作:

describe('YourService', () => {
  let service: YourService;
  
  beforeEach(() => {
    service = new YourService(
      {
        location: {hash: 'YourHash'} as any,
        ...
      } as any,
      ...
    );
  });
}

我不關心其他屬性,因此我通常向any添加類型轉換。 隨意包括所有其他屬性並適當鍵入。

如果您需要在模擬屬性上使用不同的值,您可以簡單地監視它們並使用 jasmine 的returnValue更改值:

const spy: any = spyOn((service as any)._window, 'location').and.returnValue({hash: 'AnotherHash'});

const spy: any = spyOn((service as any)._window.location, 'hash').and.returnValue('AnotherHash');

帶有內置工廠的注入代幣似乎是一種可行的方法。

我將這些用於任何全局瀏覽器,如窗口、文檔、本地存儲、控制台等。

核心/提供者/window.provider.ts

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

export const WINDOW = new InjectionToken<Window>(
    'Window',
    {
        providedIn: 'root',
        factory(): Window {
            return window;
        }
    }
);

注射:

constructor(@Inject(WINDOW) private window: Window)

單元測試:

const mockWindow = {
  setTimeout: jest.fn(),
  clearTImeout: jest.fn()
};

TestBed.configureTestingModule({
  providers: [
    {
      provide: WINDOW,
      useValue: mockWindow
    }
  ]
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM