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

Unit testing Angular2(TypeScript)中的单元测试/模拟窗口属性,unit-testing,typescript,angular,phantomjs,karma-jasmine,Unit Testing,Typescript,Angular,Phantomjs,Karma Jasmine,我正在为Angular2中的服务构建一些单元测试 在我的服务中,我有以下代码: var散列:字符串; hash=this.window.location.hash 但是,当我运行包含此代码的测试时,它将失败 利用Window的所有功能是很好的,但由于我使用的是PhantomJs,我认为这是不可能的(我也尝试过Chrome,它会产生相同的结果) 在AngularJs中,我会使用mocking$Window(或者至少是有问题的属性),但由于Angular2单元测试的文档不多,我不知道如何做到这一点

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

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

var散列:字符串;
hash=this.window.location.hash

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

利用Window的所有功能是很好的,但由于我使用的是PhantomJs,我认为这是不可能的(我也尝试过Chrome,它会产生相同的结果)

在AngularJs中,我会使用mocking$Window(或者至少是有问题的属性),但由于Angular2单元测试的文档不多,我不知道如何做到这一点


有人能帮忙吗

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

首先,向angular2提供程序注册窗口-如果您在任何地方都使用它,可能是全局性的:

import { provide } from '@angular/core';
provide(Window, { useValue: window });
这告诉angular当依赖项注入请求类型
窗口
,它应该返回全局
窗口

现在,在您使用它的地方,您将它注入到您的类中,而不是直接使用全局函数:

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;
    //...
});
导入{
之前,,
在每个供应商之前,
描述,
期待,,
信息技术
注射
注入异步
}来自“angular2/测试”;
让我的窗口:窗口;
在每个提供程序之前(()=>[
//可能会比这更能嘲弄你的东西。。
myMockWindow={location:{hash:'WAOW-MOCK-hash'}};
提供(窗口,{useValue:myMockWindow})
]);
它('应该做的事',()=>{
让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}]);
  });
让myMockWindow:Window;
在每个之前(()=>{
myMockWindow={location:{hash:'WAOW-MOCK-hash'}};
addProviders([SomeService,{Provider:Window,useValue:myMockWindow}]);
});

我花了一些时间才弄明白它是如何工作的。

在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; }) }
    ]
  });
beforeach(()=>{

让windowMock:Window=

我真的不明白为什么没有人提供最简单的解决方案,正如您所见,这是Angular团队推荐的测试服务的方法。在大多数情况下,您甚至不必处理测试床的东西

此外,您还可以对组件和指令使用这种方法。在这种情况下,您将不创建组件实例,而是创建类实例。这意味着您不必处理组件模板中使用的子组件

假设您能够将窗口注入到构造函数中

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'});


这似乎是。可能是一个XY问题,因为路由器已经存在了,抽象升级到。您也可以只注入
构造函数(window:window)
,像
provide(window,{useValue:window})
provide(window,{useClass:MyWindowMock})那样提供它
。如果有可用的类型,则无需使用字符串键。从core将其更改为
提供程序:[{provide:Window,useValue:Window},]
这是一个救命稻草。使我能够在不重定向页面的情况下模拟所需的测试。在Angular 11中工作,Angular2的发行版中不推荐使用该语法。这适用于测试,但不适用于AoT编译(编译时会出现警告,且浏览器中的应用程序会崩溃)。请使用AoT构建尝试此操作(同时在构造函数中键入
window
as
any
,以解决不同的bug),但当我试图访问另一个文件中设置的
window
上的自定义属性时,我的生产构建崩溃了。我遵循了这里的解决方案,它成功了:这是在需要时在每个测试中动态更改windowMock对象的正确答案
const spy: any = spyOn((service as any)._window.location, 'hash').and.returnValue('AnotherHash');