Angular2(TypeScript)中的单元测试/模拟窗口属性
Posted
技术标签:
【中文标题】Angular2(TypeScript)中的单元测试/模拟窗口属性【英文标题】:Unit Testing/mocking Window properties in Angular2 (TypeScript) 【发布时间】:2016-08-02 19:24:24 【问题描述】:我正在为 Angular2 中的服务构建一些单元测试。
在我的服务中,我有以下代码:
var hash: string;
hash = this.window.location.hash;
但是,当我运行包含此代码的测试时,它会失败。
利用 Window 的所有功能会很棒,但由于我使用的是 PhantomJs,我认为这是不可能的(我也尝试过 Chrome 产生相同的结果)。
在 AngularJs 中,我会求助于模拟 $Window(或至少是有问题的属性),但由于 Angular2 单元测试的文档不多,我不知道该怎么做。
有人可以帮忙吗?
【问题讨论】:
好像是quite straghtforward。可能是XY问题,因为路由器已经has the hash abstracted,抽象上升到DOM location。 【参考方案1】:在 Angular 2 中,您可以使用 @Inject()
函数通过使用字符串标记命名窗口对象来注入窗口对象,如下所示
constructor( @Inject('Window') private window: Window)
在@NgModule
中,您必须使用相同的字符串提供它:
@NgModule(
declarations: [ ... ],
imports: [ ... ],
providers: [ provide: 'Window', useValue: window ],
)
export class AppModule
那么你也可以使用token字符串来模拟它
beforeEach(() =>
let windowMock: Window = <any> ;
TestBed.configureTestingModule(
providers: [
ApiUriService,
provide: 'Window', useFactory: (() => return windowMock; )
]
);
这在 2016 年 10 月 28 日最新的 Angular 2.1.1 中有效。
不适用于 Angular 4.0.0 AOT。 https://github.com/angular/angular/issues/15640
【讨论】:
这适用于测试,但不适用于 AoT 编译(编译但带有警告并且应用程序在浏览器中崩溃) 使用 AoT 构建尝试过此操作(以及在构造函数中将window
键入为 any
以解决不同的错误),但是当我尝试访问自定义属性时,我的生产构建崩溃了window
我在另一个文件中设置。我在这里遵循了解决方案,并且它起作用了:***.com/a/37176929/1683187
这是在需要时在每个测试中动态更改 windowMock 对象的正确答案【参考方案2】:
正如评论中提到的@estus,您最好从路由器获取哈希。但是要直接回答您的问题,您需要将 window 注入您正在使用它的地方,以便在测试期间您可以模拟它。
首先,向 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;
//...
);
【讨论】:
您也可以只注入constructor(window:Window)
并像provide(Window, useValue: window)
或provide(Window, useClass: MyWindowMock)
一样提供它。如果有可用类型,则无需使用字符串键。
provide removed in RC6 从核心,将其更改为 providers: [ provide: Window, useValue: window, ]
这是救命稻草。使我能够在没有页面重定向的情况下模拟我需要的测试。在 Angular 11 中工作【参考方案3】:
在RC4方法provide()
被弃用之后,RC4之后的处理方法是:
let myMockWindow: Window;
beforeEach(() =>
myMockWindow = <any> location: <any> hash: 'WAOW-MOCK-HASH';
addProviders([SomeService, provide: Window, useValue: myMockWindow]);
);
我花了一段时间才弄清楚它是如何工作的。
【讨论】:
该语法在 Angular2 的发布版本中已被弃用【参考方案4】:我真的不明白为什么没有人提供最简单的解决方案,即 Angular 团队推荐的测试服务的方法,如您所见 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
。随意包含所有其他属性并正确键入。
如果您需要模拟属性的不同值,您可以简单地监视它们并使用 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');
【讨论】:
【参考方案5】:带有内置工厂的注入令牌似乎是要走的路。
我将这些用于任何全局浏览器,如窗口、文档、localStorage、控制台等。
core/providers/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
]
);
【讨论】:
以上是关于Angular2(TypeScript)中的单元测试/模拟窗口属性的主要内容,如果未能解决你的问题,请参考以下文章
angular2 Typescript中的Jquery不起作用
如何从 Angular2(Typescript)中的 Json 数组中获取值的总和
如何使用 Jasmine 为私有方法编写 Angular / TypeScript 单元测试