[英]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.