用sinon监视方法。方法绑定到事件侦听器。方法已执行,但.callOnce是否为假?

Sea*_*son 0 javascript backbone.js sinon

我的代码正常运行,但是我的测试用例由于其期望之一而失败。我不明白为什么我的间谍无法相信方法已经执行。

我像这样绑定事件监听器:

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();
Run Code Online (Sandbox Code Playgroud)

我正在用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();
});
Run Code Online (Sandbox Code Playgroud)

我的测试用例失败了,因为期望_onAdd一次被调用是不正确的。但是,_foo一次被调用的期望正确的。

我在监视事件监听器方面做得不正确。为什么sinon不相信_onAdd被称为。我该如何纠正?

Cub*_*Eye 5

您的问题是由于被函数引用。当您在Playlists变数中建立播放清单时,它会initialize()playlists集合上执行,而集合会建立参照的事件侦听器this._onAdd

当您监视时,它会重新定义_onAdd指向的内容,但不会更新事件侦听器正在使用的引用。考虑以下:

_onAdd: function(){...} // Call this FuncRefA
Run Code Online (Sandbox Code Playgroud)

initialize(通过new playlists();)调用when时,将对事件侦听器进行评估以指向,FuncRefA因为这是评估时所指向的_onAdd

this.on('add', FuncRefA);
Run Code Online (Sandbox Code Playgroud)

然后,当您添加间谍时,它实际上是在做这样的事情。

_onAdd: function(){ FuncRefA() } // Wraps the existing function, Call the new function FuncRefB
Run Code Online (Sandbox Code Playgroud)

在这一点上_onAdd现在指向FuncRefB,但该事件监听器是在仍然指向FuncRefA所以当你的测试运行事件侦听器被调用(和FuncRefA),但你的间谍不会被调用,因为事件侦听器不知道它的存在(它指向一个FuncRefA

有两种解决方案:

1)您可以在创建之前prototypeplaylists集合上设置间谍Playlists,这样Playlists创建时就是将功能引用给您的间谍

sinon.spy(playlists.prototype, '_onAdd');
Playlists = new playlists();
//... run tests now.
Run Code Online (Sandbox Code Playgroud)

2)使事件侦听器具有调用的匿名函数,_onAdd以便在事件发生时对其进行评估。这将允许您监视Playlists_onAdd方法,因为一旦add事件触发,它的引用就会被评估。

initialize: function(){
    var self = this;
    this.on('add', function(){ self._onAdd(arguments) });
},
Run Code Online (Sandbox Code Playgroud)