在 Javascript 中模拟 window.location.href

IT技术 javascript mocking jasmine
2021-03-14 21:28:42

我对使用 window.location.href 的函数进行了一些单元测试——不理想我宁愿将其传入,但在实现中是不可能的。我只是想知道是否可以在不实际导致我的测试运行程序页面实际转到 URL 的情况下模拟此值。

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

我正在将 jasmine 用于我的单元测试框架。

6个回答

最好的方法是在某处创建一个辅助函数,然后模拟:

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

我会提出两个解决方案,这些解决方案在之前的帖子中已经暗示过:

  • 创建一个围绕访问的函数,在你的生产代码中使用它,并在你的测试中用 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");
    
在 Jasmine 2.x 中,第一个示例中使用的语法似乎发生了一些变化。.andReturn()应替换为.and.returnValue().
2021-05-13 21:28:42

您需要模拟本地上下文并创建您自己的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!)将首先从上下文对象中查看,如果不存在,则从实际上下文中查看。

在我看来,你可以用它编写可读的测试。我认为@Kurt Harriger 提出的解决方案要好得多。-1
2021-04-23 21:28:42
with已成为过去。这个答案已经有 10 年的历史了,即便如此with也不再使用了。
2021-04-26 21:28:42
'with' statements are not allowed in strict mode.
2021-05-03 21:28:42
到底是with什么?
2021-05-05 21:28:42
with(obj) {} 语句已弃用,因此在严格模式下无效。
2021-05-07 21:28:42

有时您可能有一个修改 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 已经是对 window 的引用。在实现 Worker API 的平台中,在 Worker self 中是对全局范围的引用。在 node.js 中 self 和 window 都没有定义,所以如果你愿意,你也可以这样做:

self || window || global

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

下面是我用来模拟 window.location.href 和/或其他任何可能在全局对象上的方法。

首先,不是直接访问它,而是将它封装在一个module中,在该module中,对象由一个 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;

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

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

这将更改win窗口module中变量。它最初设置为全局window对象,但未设置为您放入的假窗口对象。所以现在替换它后,代码将获取您放置的假窗口对象及其假 href。