【问题标题】:Sinon does not seem to spy for an event handler callbackSinon 似乎没有监视事件处理程序回调
【发布时间】:2012-02-14 10:10:11
【问题描述】:

我正在使用 Jasmin、Simon 和 jasmin-simon 测试主干视图。

代码如下:

var MessageContainerView = Backbone.View.extend({
    id: 'messages',
    initialize: function() {
        this.collection.bind('add', this.addMessage, this);
    },
    render: function( event ) {
        this.collection.each(this.addMessage);
        return this;
    },
    addMessage: function( message ) {
        console.log('addMessage called', message);
        var view = new MessageView({model: message});
        $('#' + this.id).append(view.render().el);
    }
});

实际上,我所有的测试都通过了,但只有一个。我想检查是否在我向this.collection 添加项目时调用了addMessage

describe('Message Container tests', function(){
    beforeEach(function(){
        this.messageView = new Backbone.View;
        this.messageViewStub = sinon.stub(window, 'MessageView').returns(this.messageView);

        this.message1 = new Backbone.Model({message: 'message1', type:'error'});
        this.message2 = new Backbone.Model({message: 'message2', type:'success'});
        this.messages = new Backbone.Collection([
            this.message1, this.message2            
        ]); 

        this.view = new MessageContainerView({ collection: this.messages });
        this.view.render();

        this.eventSpy = sinon.spy(this.view, 'addMessage');
        this.renderSpy = sinon.spy(this.messageView, 'render');
        setFixtures('<div id="messages"></div>');
    });
    afterEach(function(){
        this.messageViewStub.restore();
        this.eventSpy.restore();
    });

    it('check addMessage call', function(){
        var message = new Backbone.Model({message: 'newmessage', type:'success'});
        this.messages.add(message);

        // TODO: this fails not being called at all
        expect(this.view.addMessage).toHaveBeenCalledOnce();
        // TODO: this fails similarly
        expect(this.view.addMessage).toHaveBeenCalledWith(message, 'Expected to have been called with `message`');
        // these pass
        expect(this.messageView.render).toHaveBeenCalledOnce();
        expect($('#messages').children().length).toEqual(1);
    });
});

如您所见,addMessage 确实被调用了。 (它会登录到控制台并按应有的方式调用this.messageView。在监视addMessage 调用时我错过了什么?

谢谢,维克托

【问题讨论】:

标签: javascript testing jasmine sinon


【解决方案1】:

我不确定,但据我了解,会发生以下情况:

  1. 您创建一个调用initialize 函数的新视图并将您的view.addMessage 绑定到您的集合。
  2. 执行此操作后,Backbone 将获取该函数并将其存储在您收藏的事件存储中。
  3. 然后你监视view.addMessage,这意味着你用一个监视函数覆盖它。这样做不会影响收集事件存储中存储的函数。

所以他们的测试存在一些问题。您的视图有很多您没有模拟出来的依赖项。您创建了一堆额外的 Backbone 模型和集合,这意味着您不仅要测试您的视图,还要测试 Backbones 集合和模型的功能。

您不应该测试collection.bind 是否可以工作,而是您已经使用参数'add', this.addMessage, this 在集合上调用了bind

initialize: function() {
    //you dont 
    this.collection.bind('add', this.addMessage, this);
},

所以,模拟集合很容易:

var messages = {bind:function(){}, each:function(){}}
spyOn(messages, 'bind');
spyOn(messages, 'each');
this.view = new MessageContainerView({ collection: messages });

expect(message.bind).toHaveBeenCalledWith('bind', this.view.addMessage, this.view);

this.view.render()

expect(message.each).toHaveBeenCalledWith(this.view.addMessage);

... and so on

这样做你只测试你的代码而不依赖于 Backbone。

【讨论】:

    【解决方案2】:

    正如安德烈亚斯在第 3 点所说:

    Then you spy on view.addMessage which means you overwrite it with a spy function. Doing this will have no effect on the function that is stored in the collection event store.

    忽略 Andreas 建议的所有令人敬畏的重构,该问题的直接答案是像这样监视MessageContainerView.prototype.addMessage

    describe('Message Container tests', function(){
        beforeEach(function(){
            this.messageView = new Backbone.View;
            this.messageViewStub = sinon.stub(window, 'MessageView').returns(this.messageView);
    
            this.message1 = new Backbone.Model({message: 'message1', type:'error'});
            this.message2 = new Backbone.Model({message: 'message2', type:'success'});
            this.messages = new Backbone.Collection([
                this.message1, this.message2            
            ]);
            // Here
            this.addMessageSpy = sinon.spy(MessageContainerView.prototype, 'addMessage');
            this.view = new MessageContainerView({ collection: this.messages });
            this.view.render();
    
            this.eventSpy = sinon.spy(this.view, 'addMessage');
            this.renderSpy = sinon.spy(this.messageView, 'render');
            setFixtures('<div id="messages"></div>');
        });
        afterEach(function(){
            this.messageViewStub.restore();
            MessageContainerView.prototype.addMessage.restore();
    
        });
    
        it('check addMessage call', function(){
            var message = new Backbone.Model({message: 'newmessage', type:'success'});
            this.messages.add(message);
    
            // TODO: this fails not being called at all
            expect(this.addMessageSpy).toHaveBeenCalledOnce();
            // TODO: this fails similarly
            expect(this.addMessageSpy).toHaveBeenCalledWith(message, 'Expected to have been called with `message`');
            // these pass
            expect(this.messageView.render).toHaveBeenCalledOnce();
            expect($('#messages').children().length).toEqual(1);
        });
    });
    

    无论如何,我确实建议实施 Andreas 的建议。 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-23
      • 1970-01-01
      • 2012-12-16
      • 2013-01-13
      • 2011-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多