18

Let's say I have a utility function that, for simplicity's sake (the real thing is complicated and irrelevant), returns the current window's querystring.

var someUtilityFunction = () {
    return window.location.search.substring(1);
};

Now I want to unit test this function in qUnit (not sure if the testing harness is relevant or not):

test('#1 someUtilityFunction works', function () {
    // setup
    var oldQS = window.location.search;
    window.location.search = '?key1=value1&key2=value2&key3=value3';

    var expectedOutput = 'key1=value1&key2=value2&key3=value3';

    // test
    equals(someUtilityFunction(),
        expectedOutput,
        'someUtilityFunction works as expected.');

    // teardown
    window.location.search = oldQS;
});

The problem here is that setting the window.location.search to a different querystring is causing the page to reload, essentially entering an infinite request loop. Is there any way to mock out the window.location object without making any changes to the someUtilityFunction function?

4

2 回答 2

19

几天前我们遇到了同样的问题。主要有2种方法:

重写你的代码

这可能不是最好的(如果有的话)解决方案,但请考虑将window对象传递给您的函数以使模拟更容易。更好的是,使用闭包并封装您的代码。这还有几个优点:

  • 您可以阴影全局变量
  • 您可以使用私有本地变量
  • 您可以避免命名冲突
  • 阴影使嘲笑变得非常容易,只需传入其他内容

包装你的代码

您可以将所有代码包装在一个将窗口对象模拟为局部变量的函数中。你基本上也有两种可能性:

假设这是模拟:

var customWindow = {
    location: {
        search: "",
        hash: ""
    }
};

使用闭包

var someUtilityFunction;

(function(window) {
    // window is now shadowed by your local variable
    someUtilityFunction = () {
        return window.location.search.substring(1);
    };
})(customWindow);

window用局部阴影遮蔽了全局window

使用with语句

虽然我通常强烈反对,但它确实可以解决这里的很多问题。由于它基本上重新映射了您的范围,因此您可以非常轻松地模拟您的环境。

// first some more preparation for our mock
customWindow.window = customWindow;

with(customWindow) {

    // this still creates the var in the global scope
    var someUtilityFunction = () {
        // window is a property of customWindow
        return window.location.search.substring(1);
    };

    // since customWindow is our scope now
    // this will work also
    someUtilityFunction = () {
        // location is a property of customWindow too
        return location.search.substring(1);
    };

}

顺便说一句:我不知道该search物业是否与该物业有相同的症状hash- 即有时包括问号,有时不包括。但您可能要考虑使用

window.location.search.replace(/^\?/, "");

代替

window.location.substr(1);
于 2012-08-09T20:36:06.427 回答
3

我使用window.history.pushState. 请参阅此 StackOverflow 答案。对于每个单元测试,我调用一个函数setQueryString('var=something'),然后像这样实现:

function setQueryString(queryString) {
  window.history.pushState({}, '', '?' + queryString);
}

您需要使用afterEachQUnit.module 的方法清除查询字符串,否则您的查询字符串将被设置为最终测试的值,您会得到奇怪的结果。

于 2015-07-09T12:19:46.117 回答