是否可以模拟 qUnit 测试的窗口位置对象

Is it possible to mock the window.location object for a qUnit test?

本文关键字:窗口 位置 对象 测试 qUnit 模拟 是否      更新时间:2023-09-26

假设我有一个实用程序函数,为了简单起见(真实的东西很复杂且无关紧要),它返回当前窗口的查询字符串。

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

现在我想在 qUnit 中对这个函数进行单元测试(不确定测试工具是否相关):

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

这里的问题是将window.location.search设置为不同的查询字符串会导致页面重新加载,实质上是进入无限请求循环。有没有办法在不对someUtilityFunction函数进行任何更改的情况下模拟 window.location 对象?

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

重写代码

这可能不是最好的(如果有的话)解决方案,但请考虑将 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);

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

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

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