在JavaScript中嘲笑window.location.href

我有一个函数,使用window.location.href的一些unit testing – 不理想我宁愿已经通过了这个,但在实施中不可能。 我只是想知道是否有可能嘲笑这个值,而实际上没有导致我的testing运行器页面实际上去的URL。

window.location.href = "http://www.website.com?varName=foo"; expect(actions.paramToVar(test_Data)).toEqual("bar"); 

我正在使用我的unit testing框架的茉莉花。

您需要模拟本地上下文并创build您自己的windowwindow.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/... 

如果使用with则所有variables(包括window !)将首先从上下文对象中查看,如果不存在,则从实际上下文中查看。

最好的办法是在某处创build一个辅助函数,然后嘲笑:

  var mynamespace = mynamespace || {}; mynamespace.util = (function() { function getWindowLocationHRef() { return window.location.href; } return { getWindowLocationHRef: getWindowLocationHRef } })(); 

现在,而不是直接在你的代码中使用window.location.href,而是使用它。 然后,只要您需要返回一个模拟值,就可以replace这个方法:

 mynamespace.util.getWindowLocationHRef = function() { return "http://mockhost/mockingpath" }; 

如果你想要一个窗口位置的特定部分,如一个查询string参数,那么也为此创build一个帮助器方法,并保持parsing出你的主代码。 茉莉花等一些框架都有testing间谍,它们不仅可以模拟函数返回所需的值,还可以validation它被称为:

 spyOn(mynamespace.util, 'getQueryStringParameterByName').andReturn("desc"); //... expect(mynamespace.util.getQueryStringParameterByName).toHaveBeenCalledWith("sort"); 

我会提出两个解决scheme,这些解决scheme已经在以前的post中暗示过了:

  • 围绕访问创build一个函数,在你的生产代码中使用这个函数,并在testing中用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"); 
  • 使用dependency injection并在testing中注入一个伪造:

     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"); 

有时你可能有一个库,修改window.location,你想让它正常工作,但也要testing。 如果是这种情况,你可以使用闭包来将你想要的引用传递给你的类库。

 /* in mylib.js */ (function(view){ view.location.href = "foo"; }(self || window)); 

然后在你的testing中,在包括你的图书馆之前,你可以重新定义自己的全局,而图书馆将使用模拟自我作为视图。

 var self = { location: { href: location.href } }; 

在你的图书馆里,你也可以做如下的事情,所以你可以在testing的任何时候重新定义自己:

 /* 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内部是对全局范围的引用。 在node.js中,自我和窗口都没有定义,所以如果你愿意,你也可以这样做:

 self || window || global 

如果node.js确实实现了Worker API,这可能会改变。

下面是我用来模拟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; 

最后,设置完成,您可以通过replace窗口对象来替代您想要置于其位置的虚拟对象来testing代码。

 fakeWindow = { location: { href: "http://google.com?x=y" } } w = require("helpers/global_window"); w.setWindow(fakeWindow); 

这将改变窗口模块中的winvariables。 它最初被设置为全局window对象,但是它并没有被设置为你放入的假窗口对象。所以现在,在你replace之后,代码将会得到你的虚假的窗口对象和假的窗口对象。

国际海事组织,这个解决scheme是cburgmer的一个小改进,它允许你replacewindow.location.href与源代码中的$ window.location.href。 当然,我使用Karma而不是Jasmine,但是我相信这种方法也适用于其中一种。 我已经添加了对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; } 

现在我可以通过在$ window中注入windowInjectable()来设置location.href,如下所示:

 function($window) { $window.location.href = "http://www.website.com?varName=foo"; } 

并在unit testing中嘲笑它看起来像:

 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,否则根据https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set

在同一页面上需要伪造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