Javascript 用西农监视一种方法。方法绑定到事件侦听器。方法已执行,但是。calledOnce是否为false?
我的代码运行正常,但是我的测试用例没有达到预期的效果。我不明白为什么我的间谍不相信已经执行了一种方法 我绑定一个事件侦听器,如下所示:Javascript 用西农监视一种方法。方法绑定到事件侦听器。方法已执行,但是。calledOnce是否为false?,javascript,backbone.js,sinon,Javascript,Backbone.js,Sinon,我的代码运行正常,但是我的测试用例没有达到预期的效果。我不明白为什么我的间谍不相信已经执行了一种方法 我绑定一个事件侦听器,如下所示: var playlists = Backbone.Collection.extend({ initialize: function(){ this.on('add', this._onAdd); }, // Method: _onAdd: function (model, collection, options
var playlists = Backbone.Collection.extend({
initialize: function(){
this.on('add', this._onAdd);
},
// Method:
_onAdd: function (model, collection, options) {
console.log('onAdd');
this._foo();
},
_foo: function(){
console.log('foo');
}
});
Playlists = new playlists();
我正在使用sinon监视我的目标:
it('should call _onAdd when adding a model to the collection', function() {
sinon.spy(Playlists, '_onAdd');
sinon.spy(Playlists, '_foo');
Playlists.add({});
expect(Playlists._onAdd.calledOnce).to.equal(true);
expect(Playlists._foo.calledOnce).to.equal(true);
Playlists._onAdd.restore();
Playlist._foo.restore();
});
我的测试用例失败,因为一次调用\u onAdd
的期望值不正确。然而,\u foo
被调用一次的期望是正确的
我在监视我的事件侦听器时做了一些不正确的事情。为什么sinon不相信已经调用了添加。如何更正此问题?您的问题是函数引用引起的。当您在
playlists
变量中创建播放列表时,它正在playlists
集合上运行initialize()
,该集合将参照设置事件侦听器
当你做一个间谍时,它会重新定义_onAdd指向什么,但不会更新事件侦听器正在使用的引用。考虑以下事项:
_onAdd: function(){...} // Call this FuncRefA
调用initialize
时(通过newplaylists();
),事件侦听器将被计算为指向FuncRefA
,因为这是添加时指向的内容
this.on('add', FuncRefA);
然后当你添加间谍时,它实际上是在做这样的事情
_onAdd: function(){ FuncRefA() } // Wraps the existing function, Call the new function FuncRefB
此时,\u onAdd
现在指向FuncRefB
,但事件侦听器仍然指向FuncRefA
,因此当测试运行时,事件侦听器正在被调用(和FuncRefA
),但您的间谍没有被调用,因为事件侦听器不知道它的存在(它指向一个FuncRefA
)
有两种解决方案:
1) 在创建播放列表
之前,您可以在播放列表
集合中的原型
上设置spy,这样在创建播放列表
时,将函数引用带到spy
sinon.spy(playlists.prototype, '_onAdd');
Playlists = new playlists();
//... run tests now.
2) 使事件侦听器具有一个匿名函数,该函数调用添加
,以便在事件发生时对其进行评估。这将允许您监视播放列表的\onAdd
方法,因为一旦触发添加
事件,就会对其引用进行评估
initialize: function(){
var self = this;
this.on('add', function(){ self._onAdd(arguments) });
},
您的问题是函数引用的原因。当您在playlists
变量中创建播放列表时,它正在playlists
集合上运行initialize()
,该集合将参照设置事件侦听器
当你做一个间谍时,它会重新定义_onAdd指向什么,但不会更新事件侦听器正在使用的引用。考虑以下事项:
_onAdd: function(){...} // Call this FuncRefA
调用initialize
时(通过newplaylists();
),事件侦听器将被计算为指向FuncRefA
,因为这是添加时指向的内容
this.on('add', FuncRefA);
然后当你添加间谍时,它实际上是在做这样的事情
_onAdd: function(){ FuncRefA() } // Wraps the existing function, Call the new function FuncRefB
此时,\u onAdd
现在指向FuncRefB
,但事件侦听器仍然指向FuncRefA
,因此当测试运行时,事件侦听器正在被调用(和FuncRefA
),但您的间谍没有被调用,因为事件侦听器不知道它的存在(它指向一个FuncRefA
)
有两种解决方案:
1) 在创建播放列表
之前,您可以在播放列表
集合中的原型
上设置spy,这样在创建播放列表
时,将函数引用带到spy
sinon.spy(playlists.prototype, '_onAdd');
Playlists = new playlists();
//... run tests now.
2) 使事件侦听器具有一个匿名函数,该函数调用添加
,以便在事件发生时对其进行评估。这将允许您监视播放列表的\onAdd
方法,因为一旦触发添加
事件,就会对其引用进行评估
initialize: function(){
var self = this;
this.on('add', function(){ self._onAdd(arguments) });
},
这对我来说真的很奇怪。如何在不修改代码以支持测试的情况下测试单例?您的两种解决方案都有意义,我也理解为什么会发生这种情况,但是#2不好,因为它使代码更加复杂,以支持间谍活动,而#1不好,因为它没有为我的应用程序传达正确的意图。播放列表只能实例化一次。@SeanAnderson:1为什么有问题?我同意2有点奇怪,但为什么在实例化之前检测原型是不好的呢?事实上,我想说,检查添加是否被称为一件奇怪的事情,为什么不测试行为而不是显式的调用序列呢?好吧,我可以通过一些重构使1工作,但我失去了通过设计强制播放列表作为一个单例的能力。目前,播放列表在其作用域内实例化自身,然后将单件公开给外部世界。重构后,父对象将需要实例化播放列表并公开其单个实例。我之所以检查_onAdd,是因为我想确保_onAdd中的一个方法没有根据给_onAdd的参数执行,而是调用了_onAdd本身。如果没有调用_onAdd,那么测试用例可能会错误地通过。这对我来说真的很奇怪。如何在不修改代码以支持测试的情况下测试单例?您的两种解决方案都有意义,我也理解为什么会发生这种情况,但是#2不好,因为它使代码更加复杂,以支持间谍活动,而#1不好,因为它没有为我的应用程序传达正确的意图。播放列表只能实例化一次。@SeanAnderson:1为什么有问题?我同意2有点奇怪,但为什么在实例化之前检测原型是不好的呢?事实上,我想说,检查ADD是否被称为一件奇怪的事情,为什么不测试它呢