单元测试/模拟Angular2中的窗口属性(TypeScript)

时间:2016-04-12 09:21:25

标签: unit-testing typescript angular phantomjs karma-jasmine

我正在为Angular2中的服务构建一些单元测试。

在我的服务中,我有以下代码:

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

但是,当我运行包含此代码的测试时,它将失败。

利用Window的所有功能会很棒,但是当我使用PhantomJs时,我认为这是不可能的(我也尝试过Chrome,它会产生相同的效果)。

在AngularJs中,我会使用模拟$ Window(或者至少是有问题的属性),但由于Angular2单元测试没有很多文档,我不知道该怎么做。

有人可以帮忙吗?

4 个答案:

答案 0 :(得分:21)

在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

答案 1 :(得分:6)

正如@estus在评论中提到的那样,您最好从路由器获取哈希值。但是要直接回答你的问题,你需要在你正在使用它的地方注入窗口,以便在测试期间你可以模拟它。

首先,使用angular2提供程序注册窗口 - 如果你在整个地方使用它,可能在某个全局:

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

当依赖注入请求类型Window时,这会告诉angular,它应该返回全局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;
    //...
});

答案 2 :(得分:3)

在RC4方法*mut std::os::raw::c_void之后它被删除了,所以在RC4之后处理这个的方法是:

provide()

我需要一段时间来弄清楚它是如何工作的。

答案 3 :(得分:0)

我真的不明白为什么没人提供最简单的解决方案,即Angular-Team推荐的一种测试服务的方法,如您所见here。在大多数情况下,您甚至根本不需要处理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。还要包括所有其他属性,并适当键入。

如果您需要在模拟属性中使用不同的值,则可以仅监视它们并使用茉莉花的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');