2012-08-09 86 views
17

比方说,我有一个效用函数,为了简单起见(真实的东西是复杂的和不相关的),返回当前窗口的查询字符串。是否可以模拟qUnit测试的window.location对象?

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设置为不同的查询字符串是导致页面重新加载,本质上进入无限的请求循环。有没有办法模拟出window.location对象没有someUtilityFunction函数做任何修改?

回答

18

几天前我们遇到了同样的问题。主要有两个途径:

重写代码

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

感谢您的回答。重写代码不会有帮助,因为它可以选择使用'window'对象 - 这种情况在测试什么时候没有通过。将函数包装在“模拟块”中会改变函数中的代码,这看起来像我可能不得不做的。 – jbabey 2012-08-10 13:42:48

+0

你以某种方式需要包装你的代码,以避免重写你的代码。我只是尝试了一个“包装”,它的效果也很好(不过它使用它的感觉不对)。然后,您可以使全局可用的自定义'窗口'对象模拟您的属性,并使其在代码中以及测试中可用。我想这就是jQuery和Zepto的做法,因为他们也将'window'和'document'传递给它们的包装器。 – 2012-08-10 20:09:14

1

我已经使用window.history.pushState一些成功。见this StackOverflow answer。对于每一个单元测试,我调用一个函数setQueryString('var=something')然后我实现这样的:

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

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

+0

不适用于我。如果我做一个''window.history.pushState(...);''并直接用'console.log(window.location)'检查它,它会给我Qunit-html页面的当前URL。你有一个示例代码? – jerik 2017-03-17 16:18:38

相关问题