Javascript中的mocking window.location.href
我对一个使用window.location.href的函数进行了一些单元测试——这并不理想,我更希望能够通过测试,但在实现中这是不可能的。我只是想知道是否可以模拟这个值,而不会导致我的测试运行程序页面实际转到URLJavascript中的mocking window.location.href,javascript,mocking,jasmine,Javascript,Mocking,Jasmine,我对一个使用window.location.href的函数进行了一些单元测试——这并不理想,我更希望能够通过测试,但在实现中这是不可能的。我只是想知道是否可以模拟这个值,而不会导致我的测试运行程序页面实际转到URL window.location.href = "http://www.website.com?varName=foo"; expect(actions.paramToVar(test_Data)).toEqual("bar"); 我将jasmine用于我的单元测试
window.location.href = "http://www.website.com?varName=foo";
expect(actions.paramToVar(test_Data)).toEqual("bar");
我将jasmine用于我的单元测试框架。您需要模拟本地上下文,并创建自己版本的
window
和window.location
对象
var localContext = {
"window":{
location:{
href: "http://www.website.com?varName=foo"
}
}
}
// simulated context
with(localContext){
console.log(window.location.href);
// http://www.website.com?varName=foo
}
//actual context
console.log(window.location.href);
// http://www.actual.page.url/...
如果将
与
一起使用,则所有变量(包括窗口
!)将首先从上下文对象中查看,如果不存在,则从实际上下文中查看。最好的方法是在某处创建一个帮助函数,然后模拟:
var mynamespace = mynamespace || {};
mynamespace.util = (function() {
function getWindowLocationHRef() {
return window.location.href;
}
return {
getWindowLocationHRef: getWindowLocationHRef
}
})();
现在,不用在代码中直接使用window.location.href,只需使用它即可。然后,您可以在需要返回模拟值时替换此方法:
mynamespace.util.getWindowLocationHRef = function() {
return "http://mockhost/mockingpath"
};
如果您需要窗口位置的特定部分,例如查询字符串参数,那么也可以为此创建帮助器方法,并将解析排除在主代码之外。一些框架(如jasmine)具有测试间谍,不仅可以模拟函数以返回所需的值,还可以验证它是否被调用:
spyOn(mynamespace.util, 'getQueryStringParameterByName').andReturn("desc");
//...
expect(mynamespace.util.getQueryStringParameterByName).toHaveBeenCalledWith("sort");
有时,您可能有一个修改window.location的库,您希望允许它正常工作,但也要进行测试。如果是这种情况,您可以使用闭包将所需的引用传递到库,如下面所示
/* in mylib.js */
(function(view){
view.location.href = "foo";
}(self || window));
然后在测试中,在包含库之前,可以全局重新定义self,库将使用模拟self作为视图
var self = {
location: { href: location.href }
};
在库中,您还可以执行以下操作,因此您可以在测试中的任何时候重新定义self:
/* in mylib.js */
var mylib = (function(href) {
function go ( href ) {
var view = self || window;
view.location.href = href;
}
return {go: go}
}());
在大多数(如果不是所有的话)现代浏览器中,self在默认情况下已经是对窗口的引用。在实现Worker API的平台中,Worker self中是对全局范围的引用。在node.js中,未定义self和window,因此如果需要,也可以执行以下操作:
self || window || global
如果node.js真的实现了Worker API,这一点可能会改变。我将提出两种解决方案,这两种解决方案在之前的文章中已经提到:
- 围绕access创建一个函数,在生产代码中使用该函数,并在测试中使用Jasmine将其存根:
var actions = { getCurrentURL: function () { return window.location.href; }, paramToVar: function (testData) { ... var url = getCurrentURL(); ... } }; // Test var urlSpy = spyOn(actions, "getCurrentURL").andReturn("http://my/fake?param"); expect(actions.paramToVar(test_Data)).toEqual("bar");
- 在您的测试中使用并注入一个假:
var _actions = function (window) { return { paramToVar: function (testData) { ... var url = window.location.href; ... } }; }; var actions = _actions(window); // Test var fakeWindow = { location: { href: "http://my/fake?param" } }; var fakeActions = _actions(fakeWindow); expect(fakeActions.paramToVar(test_Data)).toEqual("bar");
- 下面是我对mock window.location.href和/或任何其他可能在全局对象上的内容所采取的方法
首先,不要直接访问它,而是将它封装在一个模块中,在这个模块中,对象由getter和setter保存。下面是我的例子。我正在使用require,但这在这里不是必需的
define(["exports"], function(exports){
var win = window;
exports.getWindow = function(){
return win;
};
exports.setWindow = function(x){
win = x;
}
});
现在,您通常在代码中执行类似于window.location.href
的操作,现在您可以执行以下操作:
var window = global_window.getWindow();
var hrefString = window.location.href;
最后,安装完成,您可以通过用您想要的假对象替换窗口对象来测试代码
fakeWindow = {
location: {
href: "http://google.com?x=y"
}
}
w = require("helpers/global_window");
w.setWindow(fakeWindow);
这将更改窗口模块中的
win
变量。它最初设置为全局窗口对象,但未设置为您放入的假窗口对象。因此,现在在您替换它之后,代码将获得您的假窗口对象及其您放置的假href IMO,此解决方案是cburgmer的一个小改进,它允许您在源代码中将window.location.href替换为$window.location.href。虽然我使用的是因果报应,而不是茉莉花,但我相信这种方法对任何一种都有效。我还增加了对sinon的依赖
首先是服务/单件:
function setHref(theHref) {
window.location.href = theHref;
}
function getHref(theHref) {
return window.location.href;
}
var $$window = {
location: {
setHref: setHref,
getHref: getHref,
get href() {
return this.getHref();
},
set href(v) {
this.setHref(v);
}
}
};
function windowInjectable() { return $$window; }
现在,我可以通过将windowInjectable()注入$window来设置代码中的location.href,如下所示:
function($window) {
$window.location.href = "http://www.website.com?varName=foo";
}
在单元测试中模拟它看起来像:
sinon.stub($window.location, 'setHref'); // this prevents the true window.location.href from being hit.
expect($window.location.setHref.args[0][0]).to.contain('varName=foo');
$window.location.setHref.restore();
getter/setter语法可以追溯到IE 9,并且根据得到了广泛的支持。您需要在同一页面上伪造window.location.href
。
在我的例子中,这个剪子非常有效:
$window.history.push(null, null, 'http://server/#/YOUR_ROUTE');
$location.$$absUrl = $window.location.href;
$location.replace();
// now, $location.path() will return YOUR_ROUTE even if there's no such route
这对我很有用:
delete window.location;
window.location = Object.create(window);
window.location.href = 'my-url';
这是我的通用解决方案,它需要在生产代码中额外导入,但不需要依赖项注入或编写单独的包装函数,如getHref()
基本上,我们将窗口放入一个单独的文件中,然后我们的prod代码从该文件间接导入窗口
在生产环境中,windowProxy===window
。
在测试中,我们可以变异导出windowProxy
的模块,并用新的临时值模拟它
//windowProxy.js
/*
*此文件仅作为窗口对象的代理引用存在
*因此,您可以在单元测试期间模拟窗口对象。
*/
导出默认窗口;
//prod/someCode.js
从'path/to/windowProxy.js'导入windowProxy;
导出函数changeUrl(){
windowProxy.location.href=https://coolsite.com';
}
//测试/someCode.spec.js
从“../prod/someCode.js”导入{changeUrl};
将*作为windowProxy从“../prod/path/to/windowProxy.js”导入;
描述('changeUrl',()=>{
让我们打开窗户;
在每个之前(()=>{
mockWindow={};
windowProxy.default=myMockWindow;
});
之后(()=>{
windowProxy.default=窗口;
});
它('更改url',()=>{
changeUrl();
expect(mockWindow.location.href).toEqual('https://coolsite.com');
});
});
当我想调用with中的函数时,我遇到了同样的问题。如何解决?在Jasmine 2.x中,第一个示例中使用的语法似乎发生了一些变化.andReturn()
应替换为。.returnValue()
严格模式下不允许使用'with'语句。
在我看来,您不能用它编写可读的测试。我认为@Kurt Harrier提出的解决方案要好得多-1WITH(obj){}语句已弃用,因此在严格模式下无效。仅适用于jest,不适用于f