我正在尝试更好地对我的 JavaScript 进行单元测试。我有以下代码:
var categoryVal = $('#category').val();
if (categoryVal === '') {
doSomething();
}
我的测试运行器在页面上没有#category
输入,那么我将如何在此处存根/模拟 jQuery 选择器?我查看了jasmin和sinon文档,但无法弄清楚如何让它们在这里工作,因为它们的存根对对象进行操作,但事实$
并非如此。
这里的问题是,它$()
是一个使用方法返回对象的函数val()
。因此,您必须存根 $() 以返回具有方法 val 的存根对象。
$ = sinon.stub();
$.withArgs('#category').returns(sinon.stub({val: function(){}}));
但这里的主要错误是让您要测试的代码调用函数 $() 来创建新实例。为什么?最好的做法是在您的类中不创建新实例,而是将它们传递给构造函数。假设您有一个函数,该函数将从输入中获取一个值,将其加倍,然后将其写回另一个:
function doubleIt(){
$('#el2').val(('#el1').val() *2);
}
在这种情况下,您通过调用创建 2 个新对象$()
。现在你必须存根$()
返回一个模拟和一个存根。使用下一个示例,您可以避免这种情况:
function doubleIt(el1, el2){
el2.val(el1.val() *2);
}
在第一种情况下,您必须存根 $ 以返回存根,在第二种情况下,您可以轻松地将存根和间谍传递到您的函数中。
所以第二个的 sinon 测试看起来像这样:
var el1 = sinon.stub({val: function(){}});
el1.returns(2);
var el2 = sinon.spy({val: function(){}}, 'val')
doubleIt(el1, el2)
assert(el2.withArgs(4).calledOnce)
因此,由于这里没有 dom 元素,您可以简单地测试您的应用程序逻辑,而无需创建与您的应用程序中相同的 dom。
jQuery 在底层使用 css 选择器引擎 Sizzle 并且被解耦,因此只有少数几个地方可以挂钩。您可以拦截它以避免与 dom 的任何交互。
jQuery.find是一个重要的改变来响应你想要的任何东西。可以在这里使用Sinon,也可以暂时换掉这个功能。
例如
existingEngine = jQuery.find
jQuery.find = function(selector){ console.log(selector) }
$(".test")
//>> ".test"
jQuery.find = existingEngine
您还可以使用回退应用特定的捕获条件
existingEngine = jQuery.find
jQuery.find = function(selector){
if(selector=='blah'}{ return "test"; }
return existingEngine.find.apply(existingEngine, arguments)
}
在我最近的工作中,我制作了一个像 dom 节点一样响应的虚拟对象,并将其包装在一个 jQuery 对象中。然后这将正确响应 val() 并具有它期望的所有 jquery 方法。就我而言,我只是从表单中提取值。如果您正在执行实际操作,您可能需要比这更聪明,也许使用 jQuery 创建一个代表您期望的临时 dom 节点。
obj = {
value: "blah",
type: "text",
nodeName: "input",
}
$(obj).val(); // "blah"
如果您使用 Backbone.js 和 Jasmin,这里有一个很好的指南来测试您的视图。向下滚动到查看部分。
http://tinnedfruit.com/2011/04/26/testing-backbone-apps-with-jasmine-sinon-3.html
诚然,存根对对象进行操作。我猜想像这样创建一个视图存根。
this.todoViewStub = sinon.stub(window, "TodoView")
.returns(this.todoView);
只是为了以后能够渲染视图。
this.view.render();
换句话说,将 '#category' div 附加到 testrunner 的 DOM 中,以便 $ 可以对其进行操作。如果您的 '#category' div 不在 this.view 中,那么您可能只需创建一个 test.html 页面,在其中运行您的独立测试。这是 Javascript MVC 框架中的一种常见模式,我更习惯于那个 Backbone。
这是一个简单的 JMVC 应用程序结构示例:
/todo
/models
todo.js
/list
/views
init.tmpl
listItem.tmpl
list.css
list.js (Controller)
unitTest.js (Tests for your list.)
list_test.html (A html for your unit tests to run on.)
有了这个设置,如果您还没有碰巧在其中一个视图中包含“#category” div,则可以在 list_test.html 中包含它。