Question

I have a class where I bind a method on initialization as follows -

function MyClass() {
    this.onHttpCallback = _.bind(onHttpCallback, this);
}

function onHttpCallback(){
   //... 
}

How do I test if the onHttpCallback when called, is always called with an object of MyClass as context?

I am using sinon.js to mock and the following code doesn't work -

it('should be binded', function () {
    //ctrl is an object of MyClass
    var dummy_obj = {};
    var spy = sinon.spy(ctrl.onHttpCallback);
    spy.call(dummy_obj);
    spy.alwaysCalledOn(ctrl).should.be.ok;
});

Update As per the comments in the following answer, it seems like it is impossible to test the binding for a method.

My Take on the problem

//Source.js

function MyClass() {

}

MyClass.prototype.init = function(){
   this.onHttpCallback = _.bind(MyClass.onHttpCallback, this);
}


MyClass.onHttpCallback(){
   //... 
}

//Test.js

it('should bind onHttpCallback', function () {
    sinon.spy(_, 'bind');
    ctrl.init();
    _.bind.calledWith(ctrl.constructor.onHttpCallback, ctrl).should.be.ok;
    _.bind.restore();
});

Works like a charm!

Was it helpful?

Solution

In case you wonder why this changes even though you clearly bound it to be MyClass before, that's because you use call with dummy_obj on the spy.
The spy wraps the original function, so it has no concept of that function's binding. It will still accept a different binding on the wrapper function, then try to call the original with that this, which is then ignored by the original function.

var context = {foo: 'bar'};
var original = _.bind(function () { console.log(this); }, context);
var spy = function (original) {
    var spyFn = function () {
        var _this = this;
        spyFn.calledOn = function (ctx) { return ctx === _this; };
        return original.apply(this, arguments);
    };
    return spyFn;
};
var originalSpy = spy(original);
// Will call the spyFn with a different `this`, but will not affect the original binding
originalSpy.call({something: 'else'});
>> {foo: 'bar'}
// Since the spy cannot know the original binding, it will think the assumption is false.
originalSpy.calledOn(context) === false;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top