Jasmine模拟window对象

47

我该如何模拟窗口对象?我正在编写Firefox扩展程序,想要使用Jasmine进行JavaScript测试。

在我的JavaScript代码中,我有:


function submit() {
...
var url = window.arguments[0];
...
}

该代码片段是一个JavaScript函数,名为"submit()"。它包含一些未列出的代码,因此我们无法确定这个函数是用于什么目的。在这个函数中,变量"url"被赋值为"window.arguments[0]",这意味着该函数从窗口参数中获取了一个URL作为输入。

显然,如果没有从window.openDialog传递任何参数,window.arguments[0]对象不存在,因此我需要在jasmine中模拟它。

以下是我尝试使用"with"语句来模拟它:


it("应该提交到服务器", function() {
var localContext = { "window": { arguments: ["http://localhost"] }
}
with(localContext);
但我仍然收到此错误 TypeError: Cannot read property '0' of undefined,好像当测试运行时 window.arguments[0] 被真正的窗口清除了,因为如果我在测试中执行

window.arguments[0]

它会正确地打印出"http://localhost"。但是当涉及 submit() 方法时,它显示 window.argument 未定义的错误。

3个回答

65
问题在于你只是覆盖了window对象的属性。如果你可以这样做,浏览器也可以这样做。因此,通常情况下,模拟每个人都可以访问的全局对象的函数或属性并不是一个好主意,因为你永远无法确定在尝试访问它们时你的更改是否存在。
这就引出了依赖注入。这是一种常见的模式,使您的代码易于单元测试,重点在于单元。什么意思?每当您创建一个新对象或访问全局对象时,您不仅测试单元功能,还测试新创建的或全局对象的功能。为了解决这个问题,不要在单元中创建新对象,而是将它们传递进去。通常情况下,您会在构造函数中执行此操作,但是在JavaScript中,函数是具有函数体作为构造函数的对象,因此您也可以将依赖项简单地传递到函数中。
因此,在您的情况下,该函数依赖于全局窗口对象。因此,不要尝试访问全局窗口对象,而是将其作为参数传递到函数中。通过这种方式,您可以在生产代码中传递窗口对象,并在测试中传递一个带有参数属性的简单JavaScript对象:
function youWannaTest(w){
    console.log(w.arguments[0]);
}

在您的扩展中,调用该函数的方式如下:
youWannaTest(window);

在您的测试中,调用函数的方式如下:

youWannaTest({arguments: ['someValue']});

谢谢回复,那我不能模拟它吗? - toy

5
我认为依赖注入是最干净的解决方案。
你的 JS:
function submit(_window) {
    _window = _window || window
    ...
    var url = _window.arguments[0];
    ...
}

您的单元测试:

it('works like a dream', function () {
    var _window = { arguments: ['url'] }
    expect(submit(_window)).toBeTotallyAwesome()
})

2

是的,你可以模拟window对象。

首先,你需要在JS代码中做一处修改:将window替换为$window

然后,你可以使用以下代码来模拟window:

var window = {
  arguments : ['url']
};

现在在你的规格文件中:在beforeEach块中

$controller('controllerName', {$window : window});

1
这仅适用于与Angular相关的用例。 - chetan1507
1
@chetan1507 *AngularJS - bokkie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接