简体   繁体   中英

Spying on event callbacks with Jasmine

I am using Jasmine to test a Backbone.js view within a Require.js module.

Why does my test fail if I don't wrap the callback in an anonymous function?

Here's the test:

describe("view extension", function () {
    // extendedView is an instance of ExtendedView()
    spyOn(extendedView, 'onOpen');

    // open the element, which should trigger an event
    extendedView.open();

    expect(extendedView.onOpen).toBeDefined();
    expect(extendedView.onOpen).toHaveBeenCalled();
});

Failing:

var BaseView = Backbone.View.extend({
    open: function () {
        this.trigger('open');
    }
});

var ExtendedView = BaseView.extend({
    initialize: function () {
        var self = this;
        self.on('open', self.onOpen);
    },
    onOpen: function() {
        console.log('I heard myself open');
    }
});

Passing with the following revision in .initialize() :

    self.on('open', function () { self.onOpen() });

The reason it is not working is that you are installing the spy on an instance and doing this after the event handler is bound. The sequence of event is roughly like this:

  1. The instance that is going to be assigned to extendedView is created so ExtendedView 's initialize method executes self.on('open', self.onOpen); . At that time self.onOpen evaluates to the original function defined by the ExtendedView class.

  2. Then you set a spy on extendedView.onOpen . This changes the value of onOpen for extendedView (and only for extendedView ) to that of a new spy.

  3. extendedView.open() is called. Because self.on was called originally with the value of self.onOpen set for the ExtendedView class, it is this original function which is called , not the spy that now resides in extendedView.onOpen .

The reason it work when you use the anonymous function is that the anonymous function gets the value of self.onOpen whenever an open event happens, which in your test means this value is obtained after you've set the spy.

You'd have to spy on the class methods rather than on the instance. Something like:

    spyOn(ExtendedView.prototype, 'onOpen');

    [...]    

    expect(ExtendedView.prototype.onOpen).toBeDefined();
    expect(ExtendedView.prototype.onOpen).toHaveBeenCalled();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM