Sinon似乎没有监视事件处理程序回调
Sinon does not seem to spy for an event handler callback
我正在与Jasmin、Simon和Jasmin-Simon一起测试主干视图。
这是代码:
var MessageContainerView = Backbone.View.extend({
id: 'messages',
initialize: function() {
this.collection.bind('add', this.addMessage, this);
},
render: function( event ) {
this.collection.each(this.addMessage);
return this;
},
addMessage: function( message ) {
console.log('addMessage called', message);
var view = new MessageView({model: message});
$('#' + this.id).append(view.render().el);
}
});
事实上,我所有的测试都通过了,只有一次通过。每当我向this.collection
添加项目时,我想检查是否调用了addMessage
。
describe('Message Container tests', function(){
beforeEach(function(){
this.messageView = new Backbone.View;
this.messageViewStub = sinon.stub(window, 'MessageView').returns(this.messageView);
this.message1 = new Backbone.Model({message: 'message1', type:'error'});
this.message2 = new Backbone.Model({message: 'message2', type:'success'});
this.messages = new Backbone.Collection([
this.message1, this.message2
]);
this.view = new MessageContainerView({ collection: this.messages });
this.view.render();
this.eventSpy = sinon.spy(this.view, 'addMessage');
this.renderSpy = sinon.spy(this.messageView, 'render');
setFixtures('<div id="messages"></div>');
});
afterEach(function(){
this.messageViewStub.restore();
this.eventSpy.restore();
});
it('check addMessage call', function(){
var message = new Backbone.Model({message: 'newmessage', type:'success'});
this.messages.add(message);
// TODO: this fails not being called at all
expect(this.view.addMessage).toHaveBeenCalledOnce();
// TODO: this fails similarly
expect(this.view.addMessage).toHaveBeenCalledWith(message, 'Expected to have been called with `message`');
// these pass
expect(this.messageView.render).toHaveBeenCalledOnce();
expect($('#messages').children().length).toEqual(1);
});
});
正如你所看到的,addMessage
确实被称为。(它登录到控制台,它应该调用this.messageView
。在监视addMessage
调用时,我错过了什么?
谢谢,Viktor
我不确定,但据我所知,会发生以下情况:
- 您将创建一个新视图,该视图调用
initialize
函数并将view.addMessage
绑定到集合 - 执行此操作时,Backbone获取函数并将其存储在集合的事件存储中
- 然后你监视
view.addMessage
,这意味着你用间谍功能覆盖它。这样做不会影响存储在集合事件存储中的函数
所以你的测试有一些问题。您的视图有很多依赖项,但您没有模拟出来。您将创建一组附加的主干模型和集合,这意味着您不仅要测试视图,还要测试主干集合和模型功能。
您不应该测试collection.bind
是否可以工作,而应该测试您已经使用参数'add', this.addMessage, this
在集合上调用了bind
initialize: function() {
//you dont
this.collection.bind('add', this.addMessage, this);
},
因此,很容易模仿这个系列:
var messages = {bind:function(){}, each:function(){}}
spyOn(messages, 'bind');
spyOn(messages, 'each');
this.view = new MessageContainerView({ collection: messages });
expect(message.bind).toHaveBeenCalledWith('bind', this.view.addMessage, this.view);
this.view.render()
expect(message.each).toHaveBeenCalledWith(this.view.addMessage);
... and so on
这样做只测试代码,而不依赖于Backbone。
正如Andreas在第3点中所说:
Then you spy on view.addMessage which means you overwrite it with a spy function. Doing this will have no effect on the function that is stored in the collection event store.
这个问题的直接答案,不考虑Andreas建议的所有令人敬畏的重构,就是像这样监视MessageContainerView.prototype.addMessage
:
describe('Message Container tests', function(){
beforeEach(function(){
this.messageView = new Backbone.View;
this.messageViewStub = sinon.stub(window, 'MessageView').returns(this.messageView);
this.message1 = new Backbone.Model({message: 'message1', type:'error'});
this.message2 = new Backbone.Model({message: 'message2', type:'success'});
this.messages = new Backbone.Collection([
this.message1, this.message2
]);
// Here
this.addMessageSpy = sinon.spy(MessageContainerView.prototype, 'addMessage');
this.view = new MessageContainerView({ collection: this.messages });
this.view.render();
this.eventSpy = sinon.spy(this.view, 'addMessage');
this.renderSpy = sinon.spy(this.messageView, 'render');
setFixtures('<div id="messages"></div>');
});
afterEach(function(){
this.messageViewStub.restore();
MessageContainerView.prototype.addMessage.restore();
});
it('check addMessage call', function(){
var message = new Backbone.Model({message: 'newmessage', type:'success'});
this.messages.add(message);
// TODO: this fails not being called at all
expect(this.addMessageSpy).toHaveBeenCalledOnce();
// TODO: this fails similarly
expect(this.addMessageSpy).toHaveBeenCalledWith(message, 'Expected to have been called with `message`');
// these pass
expect(this.messageView.render).toHaveBeenCalledOnce();
expect($('#messages').children().length).toEqual(1);
});
});
无论如何,我确实建议执行Andreas的建议。:)
- keyup事件处理程序更改焦点不适用于快速键入
- 提示使用服务器端事件处理程序激活JavaScript
- 将事件处理程序绑定到任何可能的事件
- 正在将事件处理程序添加到不存在的类
- 在循环中附加事件处理程序时出现浏览器性能问题
- 在同一个javascript事件处理程序中调用不同的函数
- 有没有一种方法可以让内联事件处理程序在元素创建后立即执行
- 检查事件处理程序参数
- 实现延迟的jquery更改事件处理程序
- 如何使用Node.js在JavaScript模块文件之间使用事件处理程序
- 如何使jQuery的“bind”或“on”事件处理程序幂等
- 带有参数的Javascript事件处理程序
- Jquery事件处理程序仅适用于匿名函数
- 如何从另一个处理程序内部取消JavaScript事件处理程序函数的执行
- 如何在jQuery事件处理程序中存储和重用超时
- 如何向onClick事件处理程序传递一个接受参数的函数,并且仍然将该函数绑定到组件's”;这个“;上下文
- 异步处理所有事件处理程序的方法
- jsplumb中的Click事件处理程序丢失“;这个“;对象
- 构造函数中的事件处理程序与构造函数外的事件处理函数的行为不同
- 如何在事件处理程序的回调中防止Default