使用 Sinon 存根 window.location.href

Posted

技术标签:

【中文标题】使用 Sinon 存根 window.location.href【英文标题】:Stubbing window.location.href with Sinon 【发布时间】:2016-08-09 06:22:28 【问题描述】:

我正在尝试测试一些客户端代码,为此我需要使用 Mocha/Sinon 存根 window.location.href 属性的值。

到目前为止我所尝试的 (using this example):

describe('Logger', () => 
    it('should compose a Log', () => 
        var stub = sinon.stub(window.location, 'href', 'http://www.foo.com');
    );
);

跑步者显示如下错误:

TypeError:自定义存根应该是函数或属性描述符

将测试代码改为:

describe('Logger', () => 
    it('should compose a Log', () => 
        var stub = sinon.stub(window.location, 'href', 
            value: 'foo'
        );
    );
);

产生此错误:

TypeError: 试图将字符串属性 href 包装为函数

将函数作为第三个参数传递给sinon.stub 也不起作用。

有没有办法提供一个假的window.location.href 字符串,同时避免重定向(因为我在浏览器中测试)?

【问题讨论】:

【参考方案1】:

您需要使用globalbeforeEachit 中为您的测试模拟window 对象

例如

it('should compose a Log', () => 
   global.window = 
       location: 
           href: 
               value: 'foo'
           
       
   
  //.... call the funciton 
);

【讨论】:

我喜欢这个解决方案,因为它很简单,通常可以完成工作。其他选项适用于更复杂的要求。 我也是,我很高兴你发现它很有用? 如果我错了,请纠正我,但覆盖全局对象会跟随你的其余测试文件,对吗?这意味着它将被任何其他测试用例覆盖。可能不是问题,但仅供参考。 @perry 为了在测试开始时解决这个问题,我保存了全局对象const originalWindow = global.window,然后在测试结束时恢复了它global.window = originalWindow 我不确定这对你们有什么作用,因为我收到错误说不能分配给只读属性。【参考方案2】:

存根不能替换属性,只能替换函数。

抛出的错误强化了这一点:

TypeError:自定义存根应该是函数或属性描述符

来自文档:

什么时候使用存根?

在需要时使用存根:

    通过测试控制方法的行为以强制代码沿特定路径运行。示例包括强制方法抛出错误以测试错误处理。

    当您想阻止直接调用特定方法时(可能是因为它触发了不希望的行为,例如 XMLHttpRequest 或类似的行为)。

http://sinonjs.org/releases/v2.0.0/stubs/


可能的解决方案

虽然许多内置对象可以被替换(用于测试),但有些则不能。对于这些属性,您可以创建外观对象,然后您必须在代码中使用它们并能够在测试中替换它们。

例如:

var loc = 

    setLocationHref: function(newHref) 
        window.location.href = newHref;
    ,

    getLocationHref: function() 
        return window.location.href;
    

;

用法:

loc.setLocationHref('http://acme.com');

然后你可以在你的测试中写

var stub = sinon.stub(loc, 'setLocationHref').returns('http://www.foo.com');

注意链接的returns() 调用。您的代码中还有另一个错误:第三个参数必须是一个函数,而不是另一个类型的值。这是一个回调,而不是属性应该返回的内容。

See the source code of stub()

【讨论】:

谢谢@try-catch-finally 你的回答确实是正确的,我将其标记为这样,但不幸的是我需要测试一些行为直接依赖于window.location.href 值的代码。由于我使用 Mocha html 运行程序在浏览器中进行测试,是否有任何方法可以为 window.location.href 制作一个值,使被测代码可以访问该值,而无需浏览器跟随 URL? 很遗憾没有。但是我想到的另一个技巧是:配置下划线的 Web 服务器以在任何路径返回测试套件运行器,并使测试框架不断地将结果保存到本地存储中,并在页面加载(重定向)时重新加载它。当然你不应该重定向到“foo.com”,而是“/foo/dir”。请在 *** 上搜索有关此主题的类似问题,如果找不到答案,请再问一个问题(写得很清楚:)。 我们经常在 Sinon 问题跟踪器上为人们提供的另一种选择是在客户端代码中为全局变量(例如 wrapple)提供现成的外观层,然后将其方法存根。实现同样的目标。【参考方案3】:

使用window.location.assign(url) 而不是覆盖window.location 的值。然后你可以在window.location 对象上存根assign 方法。

http://www.w3schools.com/jsref/met_loc_assign.asp


更新:我在无头浏览器中对此进行了测试,但如果您在 Chrome 中运行测试,它可能无法正常工作。见@try-catch-finally's response。

【讨论】:

这解释得不好。有人可以编辑这个并给出一个真正的解释这意味着什么以及如何做到这一点? 恕我直言,这在讨论中得到了充分的解释。要详细说明他的响应,请更改客户端代码以将 window.location = url 替换为 window.location.assign(url)。然后,您可以像这样对location 对象的assign 方法存根:var stub = sinon.stub(window.location, 'assign') 这个答案是错误的。您不能重新分配(存根)location.assign,至少在 Chrome 中 window.location.assign = function() console.log("meh"); 无效。当调用location.assign() 时,我得到Uncaught TypeError: Failed to execute 'assign' on 'Location': 1 argument required, but only 0 present.(这表明本机方法抱怨缺少参数)。 感谢指正。我只在无头浏览器中尝试过这个,它对我有用。但是,如果您在 Chrome 等浏览器中运行 mocha 测试,则此方法将不起作用。

以上是关于使用 Sinon 存根 window.location.href的主要内容,如果未能解决你的问题,请参考以下文章

使用 Sinon 存根 window.location.href

用 sinon 模拟/存根猫鼬 findById

如何使用 Sinon 存根 npm 包 `phin`

使用 sinon 为 redux 操作存根闭包函数

如何使用 sinon 存根/模拟从“超类”复制的依赖项方法

用 sinon 存根,用 chai 测试