77

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

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

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

4

9 回答 9

56

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

 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");
于 2011-08-13T14:04:47.023 回答
22

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

  • 围绕访问创建一个函数,在生产代码中使用它,并在测试中使用 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");
    
于 2012-01-13T13:24:39.633 回答
10

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

于 2011-01-25T10:41:14.113 回答
6

有时您可能有一个修改 window.location 的库,并且您希望允许它正常运行但也进行测试。如果是这种情况,您可以使用闭包将所需的引用传递给您的库,例如this。

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

然后在你的测试中,在包含你的库之前,你可以在全局范围内重新定义self,库会使用mock 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,这可能会改变。

于 2011-10-15T19:14:40.693 回答
4

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

最后设置完成,您可以通过将 window 对象替换为您希望在其位置的假对象来测试您的代码。

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

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

于 2015-07-23T16:05:44.653 回答
1

这对我有用:

delete window.location;
window.location = Object.create(window);
window.location.href = 'my-url';
于 2020-03-13T11:26:06.527 回答
0

IMO,此解决方案是 cburgmer 的一个小改进,它允许您在源代码中将 window.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; }

现在我可以通过将 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,并且根据https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set得到广泛支持

于 2016-08-12T01:56:13.790 回答
0

这是我的通用解决方案,它需要在生产代码中额外导入,但不需要依赖注入或编写单独的包装函数,如getHref().

基本上我们将窗口扔到一个单独的文件中,然后我们的产品代码从该文件间接导入窗口。

在生产中,windowProxy === window. 在测试中,我们可以改变导出的模块windowProxy并用一个新的临时值模拟它。

// windowProxy.js

/*
 * This file exists solely as proxied reference to the window object
 * so you can mock the window object during unit tests.
 */
export default window;
// prod/someCode.js
import windowProxy from 'path/to/windowProxy.js';

export function changeUrl() {
  windowProxy.location.href = 'https://coolsite.com';
}
// tests/someCode.spec.js
import { changeUrl } from '../prod/someCode.js';
import * as windowProxy from '../prod/path/to/windowProxy.js';

describe('changeUrl', () => {
  let mockWindow;
  beforeEach(() => {
    mockWindow = {};
    windowProxy.default = myMockWindow;
  });

  afterEach(() => {
    windowProxy.default = window;
  });

  it('changes the url', () => {
    changeUrl();
    expect(mockWindow.location.href).toEqual('https://coolsite.com');
  });
});
于 2021-01-04T17:46:24.457 回答
-1

您需要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
于 2016-08-30T21:09:27.053 回答