20

在尝试测试视图是否正确绑定到事件时,我遇到了一些有趣的困难。在骨干网中,我们通常使用如下方式绑定到初始化方法中的事件:something.bind("change", this.render);. 在我的测试中,我想确保设置了这个绑定,所以我做了以下事情: 

this.myView = new MyView();
spyOn(this.myView, "render");;
this.legendView.groupData.trigger("change");
expect(this.legendView.render).toHaveBeenCalled();

但是,那是行不通的。因为绑定发生在 MyView 的初始化函数中,所以事件在那个时间绑定到了 myView 的渲染函数。因此,当您添加间谍时,它会包装渲染函数并将其设置回 myView.render 中的位置。但是第一次绑定创建的闭包仍然存在,我们完全被骗了。那么我们能做些什么呢?我所做的是将我的绑定调用移动到一个单独的函数,例如: 

myView = Backbone.View.extend({
initialize: function(){
    _.bindAll(this, "render");
    this.initialize_model_bindings();
},
initialize_model_bindings: function(){
    something.bind("change", this.render);
},
render: function(){ //... }
});

然后我的测试看起来像:

this.myView = new MyView();
spyOn(this.myView, "render");
this.myView.initialize_model_bindings();
this.legendView.groupData.trigger("change");
expect(this.legendView.render).toHaveBeenCalled();

这可行,但我正在寻找更好的解决方案。谢谢

4

6 回答 6

12

I have managed to achieve this using prototype patching. Before you create the instance of the view, spyOn the constructor's prototype.

spyOn(MyView.prototype, 'changeSelected');
var view = new MyView();
view.selectSomething();
expect(view.changeSelected).toHaveBeenCalled();
于 2011-09-23T14:08:48.237 回答
5

与其监视回调,不如尝试监视 something.bind。然后测试是否使用适当的参数调用了绑定。到目前为止,这对我有用。我正在使用 sinon.js 而不是 jasmine 的内置间谍。sinon.js 使测试传递给相同方法调用堆栈中的方法调用的参数变得更容易一些(例如,在视图初始化中绑定的一堆调用)。所以我没有单独用茉莉花测试过这个想法,但相信它应该是可能的。

spyOn(this.legendView.groupData, 'bind');
this.myView = new MyView();
expect(this.legendView.groupData.mostRecentCall.args).toEqual('change', this.myView.render); // example!! only works if testing a single call to bind or the last call in a series (ie mostRecentCall)

和 / sinon.js

sinon.spy(this.legendView.groupData, 'bind');
this.myView = new MyView();
expect(this.legendView.groupData.bind.calledWith('change', this.myView.render); // works w/ any number of calls to bind
于 2011-07-11T20:36:37.303 回答
3

我通过监视我的渲染函数调用的函数解决了这个问题。所以在你的例子中:

myView = Backbone.View.extend({
  initialize: function(){
      _.bindAll(this, "render");
      something.bind("change", this.render);
  },
  someOtherFunction: function(){},  //this function only called from render
  render: function(){ this.someOtherFunction(); /* rest of render function */ }
});

测试看起来像:

this.myView = new MyView();
spyOn(this.myView, "someOtherFunction");
this.myView.something.trigger("change");
expect(this.myView.someOtherFunction).toHaveBeenCalled();  

然后我为 someOtherFunction 所做的任何事情编写了一个单独的测试。

于 2011-07-20T16:15:34.193 回答
2

您应该考虑查看 Sinon.js。您可以存根/模拟 render() 调用,甚至不必担心“someOtherFunction()”。

于 2011-07-31T23:32:03.733 回答
1

这可能与 Backbone 内部的耦合过于紧密,但您可以手动检查回调链:

expect(this.legendView.groupData._callbacks['change']).toContain(this.myView.render)
于 2011-08-08T14:25:52.167 回答
0

我遇到了同样的问题,并从以下位置更改了我的视图代码:

this.model.on('change', this.render, this);

至:

this.model.on('change', function () {
    this.render();
}, this);

我的茉莉花测试按预期工作。

于 2012-10-26T13:51:07.703 回答