2

Basically, if you mock an object using Sinon.js which has a property that's passed as callback to jQuery.delegate your mock expectations will fail when you trigger the method being observed in jQuery.delegate. If you execute the method inside of the body of an anonymous function that you use as the callback to jQUery.delegate your expectations will pass.

test("FAILS: Handler is invoked as callback from jQuery delegate method directly...", function() {
    var Controller,
        View,
        $el,
        c,
        v,
        mock;

    Controller = function() {
        var self = {};

        self.handler = function(e) {
            console.log("got handled");
        };

        return self;
    };

    View = function($el, controller) {
        var self = {};

        self.render = function() {
            $el.html("");

            $el.delegate("#derp", "keyup", controller.handler);
        };

        return self;
    };

    $el  = jQuery("");
    c    = new Controller();
    v    = new View($el, c);
    mock = this.mock(c);

    v.render();

    mock.expects("handler").once();

    $el.find("input").val("bar").trigger("keyup");
    equal($el.find("input").val(), "bar"); // passes!

    mock.verify();
});
test("WINS: Handler is invoked inside anonymous function...", function() {
    var Controller,
        View,
        $el,
        c,
        v,
        mock;

    Controller = function() {
        var self = {};

        self.handler = function(e) {
            console.log("got handled");
        };

        return self;
    };

    View = function($el, controller) {
        var self = {};

        self.render = function() {
            $el.html("");

            $el.delegate("#derp", "keyup", function(e) {
                controller.handler(e);
            });
        };

        return self;
    };

    $el  = jQuery("");
    c    = new Controller();
    v    = new View($el, c);
    mock = this.mock(c);

    v.render();

    mock.expects("handler").once();

    $el.find("input").val("bar").trigger("keyup");
    equal($el.find("input").val(), "bar"); // passes!

    mock.verify();
});

Am I doing something wrong here?

Thanks,

Erin

  • Have to agree, if v2 works then so should v1. Does v1 also fail with an actual keyup? Does v1 also fail when tested with `.triggerHandler("keyup")`? – Beetroot-Beetroot Mar 26 '12 at 10:47

1 Answers1

0

This is a classic callback scope gotcha. Function arguments are out of scope, so controller does not have a handler method since it is not defined locally. The anonymous function gives it scope because functions control scope in JavaScript.

Community
  • 1
  • 1
Paul Sweatte
  • 24,148
  • 7
  • 127
  • 265