存根jQuery选择器调用?

时间:2011-12-15 17:13:10

标签: javascript jquery unit-testing jasmine sinon

我正在努力改进我的JavaScript单元测试。我有以下代码:

var categoryVal = $('#category').val();
if (categoryVal === '') { 
    doSomething();
} 

我的测试运行器在页面上没有#category输入,那么我如何在这里存根/模拟jQuery选择器呢?我查看了jasminsinon文档,但无法弄清楚如何让它们在这里工作,因为它们的存根操作对象,$不是。

3 个答案:

答案 0 :(得分:35)

这里的问题是$()是一个返回方法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);
}

虽然在第一种情况下你必须以stub来返回存根,但在第二种情况下,你可以轻松地将存根和间谍传递给你的函数。

所以第二个的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。

答案 1 :(得分:10)

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"

答案 2 :(得分:1)

如果您使用的是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.)

有了这个设置,你可以在list_test.html中包含“#category”div,如果你还没有把它放在其中一个视图中。