Unit testing Angular2(TypeScript)中的单元测试/模拟窗口属性
我正在为Angular2中的服务构建一些单元测试 在我的服务中,我有以下代码: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单元测试的文档不多,我不知道如何做到这一点
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
asany
,以解决不同的bug),但当我试图访问另一个文件中设置的window
上的自定义属性时,我的生产构建崩溃了。我遵循了这里的解决方案,它成功了:这是在需要时在每个测试中动态更改windowMock对象的正确答案
const spy: any = spyOn((service as any)._window.location, 'hash').and.returnValue('AnotherHash');