【问题标题】:backbone.js click event spy is not getting called using jasmine.js and sinon.js没有使用 jasmine.js 和 sinon.js 调用主干.js 点击事件间谍
【发布时间】:2012-02-25 03:47:36
【问题描述】:

我正在尝试使用backbone.js、jasmine.js 和sinon.js 测试按钮点击。但是下面的测试用例失败了。我正在使用间谍来跟踪它是否被调用。 你能帮我解决这个问题吗?

谢谢。

新任务模板

<script id='new_task_template' type='text/template'>
  <input type='text' id='new_task_name' name='new_task_name'></input>
  <button type='button' id='add_new_task' name='add_new_task'>Add Task</button>
</script>

新任务视图

T.views.NewTaskView = Backbone.View.extend({
  tagName: 'section',
  id: 'new_task_section',
  template : _.template ( $("#new_task_template").html() ),
  initialize: function(){
    _.bindAll( this, 'render', 'addTask');
  },
  events:{
    "click #add_new_task" : "addTask"
  },
  render: function(){
    $(this.el).html( this.template() );
    return this;
  },
  addTask: function(event){
    console.log("addTask");
  }
});

Jasmine 测试用例

describe("NewTaskView", function(){
  beforeEach( function(){    
    this.view = new T.views.NewTaskView();
    this.view.render();
  });

  it("should #add_new_task is clicked, it should trigger the addTask method", function(){
    var clickSpy = sinon.spy( this.view, 'addTask');
    $("#add_new_task").click();
    expect( clickSpy ).toHaveBeenCalled();
  });
});

茉莉花输出

NewTaskView
  runEvents
    runshould #add_new_task is clicked, it should trigger the addTask method
      Expected Function to have been called.

【问题讨论】:

  • 视图在 DOM 中的什么位置呈现自身?模板甚至在规范中可用吗?可能 button#add_new_task 在 Jasmine 规范运行器的 DOM 中不存在,因此 $("#add_new_task").click(); 无效。如果您确定使用正确的模板呈现视图,则可以使用 NewTaskView 的元素作为 jquery 函数的上下文:$('#add_new_task', this.view.el).click();
  • 我想这个问题已经在这里回答了:stackoverflow.com/questions/8441612/…

标签: javascript jquery backbone.js jasmine sinon


【解决方案1】:

问题是你在主干已经将点击事件直接绑定到 addTask 函数之后添加你的间谍(它在视图的构建过程中这样做)。因此你的间谍不会被调用。

尝试将间谍附加到视图的原型在构建它之前。像这样:

this.addTaskSpy = sinon.spy(T.views.NewViewTask.prototype, 'addTaskSpy');
this.view = new T.views.NewTaskView();

然后记得删除它:

T.views.NewViewTask.prototype.addTaskSpy.restore()

【讨论】:

  • 这个答案应该被接受。谢谢,真是头疼!
  • 完全有道理!谢谢! :)
  • 我不清楚的是:如果我只想窥探原型的一个实例,为什么我需要窥探原型。这不可能吗?
  • 这很好用。比必须更改我的代码来处理我的测试要好得多。
  • +1 谢谢!只是一个小注释,因为监视的方法是 addTask 它应该是:this.addTaskSpy = sinon.spy(T.views.NewViewTask.prototype, 'addTask');T.views.NewViewTask.prototype.addTask.restore()
【解决方案2】:
events:{
    "click" : "addTask"
},

意味着您将点击事件绑定到this.el - 视图的根元素,在您的情况下具有 ID new_task_section。您需要将其绑定到#add_new_task,我认为这是您的“添加任务”按钮 - 这应该可以解决它!

events:{
    "click #add_new_task" : "addTask"
},

更新:

$("#add_new_task") 不会找到元素,因为视图没有添加到文档 DOM 树中。使用this.view.$('#add_new_task'),它应该可以工作,因为它将搜索存储在视图中的分离片段中的元素。

【讨论】:

  • 实际上我也有这个“点击#add_new_task”并且事件被触发,因为我在addTask方法中有console.log行。但是 expect(spy).toHaveBeenCalled() 失败;可能是我的绑定错误?
  • 如果#add_new_task不在这个视图中,是不是绑定错地方了?
  • jQuery 的工作方式是搜索附加到文档的 DOM 树,但如果您自己不将 Backbone 视图元素附加到 DOM 树,它根本就不存在。这就是为什么您需要使用 Backbone jQuery find(Backbone.View $ 方法),它将搜索的上下文设置为您的 View 的 el,从而可以在尚未附加到 DOM 树的一些 DOM 中进行搜索. #add_new_task 恰好在视图中 - 只是视图不在文档中 - 它只是保存在内存中并等待插入
  • 尝试在渲染后执行$(body).append(this.view.el),你会发现它应该可以在不将 $('#add_new_task') 更改为 this.view.$('#add_new_task') 的情况下工作(或者你可以写 $('#add_new_task', this.view.el) 如果你更喜欢)。这是 100% 正确的行为
  • jasmine-jquery 助手可以帮助您处理固定装置:github.com/velesin/jasmine-jquery
【解决方案3】:

您的方法存在一些问题。首先,您监视要测试的类,这不是单元测试应该工作的方式,因为您测试的是类的内部逻辑而不是其行为。其次,这就是您的测试失败的原因,您没有将视图 el 附加到 DOM 中。因此,要么将 el 附加到 DOM,要么直接将 click 事件触发到 el:$('#add_new_task', this.view.el).click()

顺便说一句。主干创建元素和绑定事件的方式使得编写好的单元测试变得困难,因为你不得不使用 DOM 和 jquery。编写可测试代码的更好方法是始终将所有依赖项传递给构造函数,并且不要在代码中创建新实例,因为这会导致难以测试这些对象。因此,在您的情况下,将构造函数中的 el 对象作为 jquery 对象并手动注入事件会容易得多。这样做你可以在不依赖 DOM 或 jquery 的情况下测试你的类。

所以在你的情况下,构造函数看起来像这样:

initialize: function(){
   this.el.click(_.bind( this, 'addTask'));
}

你的测试:

var el = {click: function(){}};
spyOn( el, 'click');
new T.views.NewTaskView({el: el});
expect(el.click).toHaveBeenCalled(); //test the click event was bind
//call the function that was bind to the click event, 
//which is the same as trigger the event on a real DOM object
el.click.mostRecentCall.args[0]() 

毕竟,您必须决定哪种方法适合您的需求。带有主干助手的更精简的代码或对 jquery、DOM 和主干的依赖更少的更好的可测试代码。

【讨论】:

  • Jasmine 不是单元测试框架 - 它是 BDD 框架,他所做的非常好 - 测试行为
  • 不确定你的意思。监视您要测试的课程没有多大意义。导致它不是你的类的行为来调用自身的特定函数。更改此函数的名称会破坏测试,但不会破坏行为。另一种想法是,我认为最好尽可能多地模拟出依赖项。使用依赖注入会更容易,因为您不必在测试之前加载固定装置,在其上触发点击事件并在之后进行清理。
猜你喜欢
  • 1970-01-01
  • 2016-01-03
  • 2012-07-16
  • 2012-10-11
  • 1970-01-01
  • 1970-01-01
  • 2011-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多