QUnit,Sinon.js&安培;骨干单元测试无奈:兴农间谍似乎无法检测骨干示范事件回调 [英] QUnit, Sinon.js & Backbone unit test frustration: sinon spy appears to fail to detect Backbone Model event callbacks
问题描述
在下面的单元测试code:
TestModel = Backbone.Model.extend({
默认值:{
选择:空
},
初始化:功能(){
this.on('的变化:选择,this.doSomething);
},
DoSomething的:功能(){
的console.log(有些事情已经做了。);
}
});模块(测试,{
设置:功能(){
this.testModel =新TestModel();
}
});测试(内部模型事件绑定功能(){
this.spy(this.testModel,'doSomething的');
OK(this.testModel.doSomething.called!);
this.testModel.doSomething();
OK(this.testModel.doSomething.calledOnce);
this.testModel.set('选择','新事物');
OK(this.testModel.doSomething.calledTwice); //这个测试应该过去,而是失败。控制台显示了两个有些事情已经做了的日志。
});
第三确定出现故障时,即使该功能是有效地从骨干事件绑定调用,由控制台作为demo'd。
这是非常令人沮丧,动摇了我对sinon.js是否适合我的测试应用程序的骨干信心。我做得不对,还是这与兴农如何检测是否事已调用的一个问题?有没有解决办法?
编辑:这里有一个解决方案,我的具体的例子,根据公认的答案的猴子打补丁方法。虽然其在测试本身额外的设置code几行,(我不需要的模块功能更多),它能够完成任务。谢谢,亩太短
试验(内部模型事件绑定功能(){
VAR认为这=;
VAR的init = TestModel.prototype.initialize;
TestModel.prototype.initialize =功能(){
that.spy(这一点,DoSomething的');
init.call(本);
}; this.testModel =新TestModel();
。 。 。 //测试通过!
});
调用 this.spy(this.testModel,DoSomething的')
替换 testModel.doSomething
方法用一个新的包装方法:
VAR间谍= sinon.spy(对象方面,办法);
创建用于
的object.method
间谍和间谍取代了原来的方法。
块引用>所以
this.spy(this.testModel,DoSomething的')
有效地做这样的事情:VAR M = this.testModel.doSomething;
this.testModel.doSomething =功能(){
//间谍的东西放在这里...
返回m.apply(这一点,参数);
};这意味着
testModel.doSomething
是一个不同的功能,当你在绑定事件处理程序初始化
:this.bind('的变化:选择,this.doSomething);
比它是你已经附加了间谍后。骨干事件调度程序调用原始的
DoSomething的
方法,但一个不具备兴农仪器。当你调用DoSomething的
手动,你调用了新功能谍
添加一个确实有兴农仪器如果你想使用兴农,以测试你的骨干事件,那么你就必须要安排有兴农
应用于模型谍
电话绑定任何事件之前处理程序和这可能意味着挂钩到初始化
。也许你可以猴子修补模型
初始化
添加必要间谍
要求其绑定任何事件处理程序之前,VAR的init = Model.prototype.initialize;
Model.prototype.initialize =功能(){
//设置间谍的东西...
init.apply(这一点,参数);
};演示: http://jsfiddle.net/ambiguous/C4fnX/1/
您也可以尝试的东西,如继承模型:
VAR模型= Backbone.Model.extend({});
VAR TestModel = Model.extend({
初始化:功能(){
//设置间谍的东西...
Model.prototype.initialize.apply(这一点,参数);
}
});然后使用
TestModel
代替型号,这将给你模式
的Instrumented版本的TestModel
,而不必包含正常生产就绪<code>模型内部一串特定的测试,$ C $的C 。不足之处是其他任何东西,使用模式
将需要被继承/补丁/ ...使用TestModel
代替演示: http://jsfiddle.net/ambiguous/yH3FE/1/
您可能能够获得与周围的
TestModel
问题:VAR OriginalModel =模型;
模型= Model.extend({
初始化:功能(){
//设置间谍的东西...
OriginalModel.prototype.initialize.apply(这一点,参数);
}
});但你必须得到正确的顺序,以确保每个人都使用新的
模式
,而不是旧的。演示: http://jsfiddle.net/ambiguous/u3vgF/1/
In the following unit test code:
TestModel = Backbone.Model.extend({ defaults: { 'selection': null }, initialize: function() { this.on('change:selection', this.doSomething); }, doSomething: function() { console.log("Something has been done."); } }); module("Test", { setup: function() { this.testModel = new TestModel(); } }); test("intra-model event bindings", function() { this.spy(this.testModel, 'doSomething'); ok(!this.testModel.doSomething.called); this.testModel.doSomething(); ok(this.testModel.doSomething.calledOnce); this.testModel.set('selection','something new'); ok(this.testModel.doSomething.calledTwice); //this test should past, but fails. Console shows two "Something has been done" logs. });
The third ok fails, even though the function was effectively called from the backbone event binding, as demo'd by the console.
This is very frustrating and has shaken my confidence on whether sinon.js is suitable for testing my backbone app. Am I doing something wrong, or is this a problem with how sinon detects whether something has been called? Is there a workaround?
EDIT: Here's a solution to my specific example, based on the monkey patch method of the accepted answer. While its a few lines of extra setup code in the test itself, (I don't need the module function any more) it gets the job done. Thanks,
mu is too short
test("intra-model event bindings", function() { var that = this; var init = TestModel.prototype.initialize; TestModel.prototype.initialize = function() { that.spy(this, 'doSomething'); init.call(this); }; this.testModel = new TestModel(); . . . // tests pass! });
解决方案Calling
this.spy(this.testModel, 'doSomething')
replaces thetestModel.doSomething
method with a new wrapper method:var spy = sinon.spy(object, "method");
Creates a spy for
object.method
and replaces the original method with the spy.So
this.spy(this.testModel, 'doSomething')
is effectively doing something like this:var m = this.testModel.doSomething; this.testModel.doSomething = function() { // Spying stuff goes here... return m.apply(this, arguments); };
This means that
testModel.doSomething
is a different function when you bind the event handler ininitialize
:this.bind('change:selection', this.doSomething);
than it is after you've attached your spying. The Backbone event dispatcher will call the original
doSomething
method but that one doesn't have the Sinon instrumentation. When you calldoSomething
manually, you're calling the new function thatspy
added and that one does have the Sinon instrumentation.If you want to use Sinon to test your Backbone events, then you'll have to arrange to have the Sinon
spy
call applied to the model before you bind any event handlers and that probably means hooking intoinitialize
.Maybe you could monkey-patch your model's
initialize
to add the necessaryspy
calls before it binds any event handlers:var init = Model.prototype.initialize; Model.prototype.initialize = function() { // Set up the Spy stuff... init.apply(this, arguments); };
Demo: http://jsfiddle.net/ambiguous/C4fnX/1/
You could also try subclassing your model with something like:
var Model = Backbone.Model.extend({}); var TestModel = Model.extend({ initialize: function() { // Set up the Spy stuff... Model.prototype.initialize.apply(this, arguments); } });
And then use
TestModel
instead of Model, this would give you an instrumented version ofModel
inTestModel
without having to include a bunch of test-specific code inside your normal production-readyModel
. The downside is that anything else that usesModel
would need to be subclassed/patched/... to useTestModel
instead.Demo: http://jsfiddle.net/ambiguous/yH3FE/1/
You might be able to get around the
TestModel
problem with:var OriginalModel = Model; Model = Model.extend({ initialize: function() { // Set up the Spy stuff... OriginalModel.prototype.initialize.apply(this, arguments); } });
but you'd have to get the ordering right to make sure that everyone used the new
Model
rather than the old one.Demo: http://jsfiddle.net/ambiguous/u3vgF/1/
这篇关于QUnit,Sinon.js&安培;骨干单元测试无奈:兴农间谍似乎无法检测骨干示范事件回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!