我对一个使用 window.location.href 的函数进行了一些单元测试——这并不理想,我宁愿将其传递进去,但在实现中这是不可能的。我只是想知道是否可以模拟这个值而不实际导致我的测试运行器页面实际转到该 URL。

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

我使用 jasmine 作为我的单元测试框架。

有帮助吗?

解决方案

您需要模拟本地上下文并创建自己的版本 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 然后所有变量(包括 window!)首先是从上下文对象中查看的,如果不存在,则从实际上下文中进行查看。

其他提示

最好的方法是在某处创建一个辅助功能,然后嘲笑:

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

我将提出两种解决方案,这些解决方案已经在以前的帖子中暗示:

  • 围绕访问创建一个函数,在生产代码中使用该功能,并在测试中与茉莉花固执:

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

有时,您可能会有一个修改窗口的库。Location,您想允许它正常运行,但也需要进行测试。如果是这种情况,您可以使用封闭来将所需的引用传递到图书馆。

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

然后在您的测试中,在包含库之前,您可以在全球重新定义自我,并且图书馆将使用模拟自我作为视图。

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

在您的库中,您也可以执行以下操作,因此您可以在测试中的任何时候重新定义自我:

/* in mylib.js */
var mylib = (function(href) {
    function go ( href ) {
       var view = self || window;
       view.location.href = href;
    }
    return {go: go}
}());

在大多数现代浏览器中,默认情况下,自我已经是对窗口的引用。在实现工人API的平台中,在工人自我内是对全球范围的引用。在node.js中,self和窗口均未定义,因此,如果您愿意,也可以执行此操作:

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;

最后,设置完成,您可以通过将窗口对象替换为您想要的假对象来测试您的代码。

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

这将改变 win 窗口模块中的变量。最初是设置为全局的 window 对象,但它没有设置为您放入的假窗口对象。所以现在在你替换它之后,代码将获取你的假窗口对象和你放置的假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; }

现在,我可以通过将WindowSInjectable()作为$ window将windownoctable()设置为代码中的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,否则根据 https://developer.mozilla.org/en-us/docs/web/javascript/reference/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
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top