QUnit,Sinon.js&安培;骨干单元测试无奈:兴农间谍似乎无法检测骨干示范事件回调 [英] QUnit, Sinon.js & Backbone unit test frustration: sinon spy appears to fail to detect Backbone Model event callbacks

查看:181
本文介绍了QUnit,Sinon.js&安培;骨干单元测试无奈:兴农间谍似乎无法检测骨干示范事件回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的单元测试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 the testModel.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 in initialize:

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 call doSomething manually, you're calling the new function that spy 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 into initialize.

Maybe you could monkey-patch your model's initialize to add the necessary spy 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 of Model in TestModel without having to include a bunch of test-specific code inside your normal production-ready Model. The downside is that anything else that uses Model would need to be subclassed/patched/... to use TestModel 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆