QUnit、Sinon.js &Backbone 单元测试失败:sinon spy 似乎无法检测到 Backbone Model 事件回调 [英] QUnit, Sinon.js & Backbone unit test frustration: sinon spy appears to fail to detect Backbone Model event callbacks

查看:17
本文介绍了QUnit、Sinon.js &Backbone 单元测试失败:sinon spy 似乎无法检测到 Backbone Model 事件回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在以下单元测试代码中:

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

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

The third ok fails, even though the function was effectively called from the backbone event binding, as demo'd by the console.

这很令人沮丧,动摇了我对 sinon.js 是否适合测试我的主干应用程序的信心.我做错了什么,或者这是sinon如何检测是否有东西被调用的问题?有解决方法吗?

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?

这是我的具体示例的解决方案,基于已接受答案的猴子补丁方法.虽然它在测试本身中有几行额外的设置代码,(我不再需要模块功能)它完成了工作.谢谢,mu 太短了

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

推荐答案

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

Calling this.spy(this.testModel, 'doSomething') replaces the testModel.doSomething method with a new wrapper method:

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

object.method 创建一个 spy,并用 spy 替换原来的方法.

Creates a spy for object.method and replaces the original method with the spy.

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

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

这意味着当您在 initialize 中绑定事件处理程序时,testModel.doSomething 是一个不同的函数:

This means that testModel.doSomething is a different function when you bind the event handler in initialize:

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

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

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.

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

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.

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

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

演示: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);
    }
});

然后使用 TestModel 而不是 Model,这将为您提供 TestModel 中的 Model 检测版本,而无需包含一堆在正常的生产就绪 Model 中测试特定的代码.缺点是使用 Model 的任何其他东西都需要被子类化/修补/...以使用 TestModel 代替.

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.

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

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

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

但是您必须正确排序以确保每个人都使用新的Model 而不是旧的.

but you'd have to get the ordering right to make sure that everyone used the new Model rather than the old one.

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

这篇关于QUnit、Sinon.js &Backbone 单元测试失败:sinon spy 似乎无法检测到 Backbone Model 事件回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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