6

在以下单元测试代码中:

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.
});

第三个 ok 失败,即使该函数是从主干事件绑定中有效调用的,正如控制台演示的那样。

第三次测试失败 在此处输入图像描述

这非常令人沮丧,并动摇了我对 sinon.js 是否适合测试我的骨干应用程序的信心。我做错了什么,或者这是 sinon 如何检测是否已调用某事的问题?有解决方法吗?

编辑:这是我的具体示例的解决方案,基于已接受答案的猴子补丁方法。虽然它在测试本身中有几行额外的设置代码,(我不再需要模块功能)它完成了工作。谢谢,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!
}); 
4

1 回答 1

12

调用用新的包装方法this.spy(this.testModel, 'doSomething')替换该testModel.doSomething方法:

var spy = sinon.spy(object, "method");

为 spy 创建一个 spyobject.method并用 spy 替换原始方法。

所以this.spy(this.testModel, 'doSomething')有效地做这样的事情:

var m = this.testModel.doSomething;
this.testModel.doSomething = function() {
    // Spying stuff goes here...
    return m.apply(this, arguments);
};

这意味着testModel.doSomething当您在以下位置绑定事件处理程序时,这是一个不同的功能initialize

this.bind('change:selection', this.doSomething);

而不是在你附加了你的间谍之后。Backbone 事件调度程序将调用原始doSomething方法,但该方法没有 Sinon 检测。当您doSomething手动调用时,您调用的是spy添加的新函数,并且该函数确实具有 Sinon 检测。

如果您想使用 Sinon 测试您的 Backbone 事件,那么您必须spy在绑定任何事件处理程序之前安排将 Sinon 调用应用于模型,这可能意味着挂钩到initialize​​.

也许您可以在绑定任何事件处理程序之前对模型进行猴子补丁initialize以添加必要的调用:spy

var init = Model.prototype.initialize;
Model.prototype.initialize = function() {
    // Set up the Spy stuff...
    init.apply(this, arguments);
};

演示:http: //jsfiddle.net/ambiguous/C4fnX/1/

您还可以尝试使用以下方法对模型进行子类化:

var Model = Backbone.Model.extend({});
var TestModel = Model.extend({
    initialize: function() {
        // Set up the Spy stuff...
        Model.prototype.initialize.apply(this, arguments);
    }
});

然后使用TestModel而不是 Model,这将为您提供 in 的检测版本,ModelTestModel不必在您的正常 production-ready 中包含一堆特定于测试的代码Model。不利的一面是,任何其他使用的东西都Model需要被子类化/修补/...才能使用TestModel

演示:http: //jsfiddle.net/ambiguous/yH3FE/1/

您可能可以通过以下方式解决TestModel问题:

var OriginalModel = Model;
Model = Model.extend({
    initialize: function() {
        // Set up the Spy stuff...
        OriginalModel.prototype.initialize.apply(this, arguments);
    }
});

但是您必须获得正确的订购权,以确保每个人都使用新的Model而不是旧的。

演示:http: //jsfiddle.net/ambiguous/u3vgF/1/

于 2012-06-02T20:11:28.070 回答