Home > English, JavaScript, Programming > Testing events on jQuery objects with Jasmine

Testing events on jQuery objects with Jasmine

Recently we had a piece of JavaScript code that looked roughly like this:

myApp.buttonBinder = {
  bind: function(button) {
    if (myApp.shouldChangeButtonAction()) {
      button.unbind("click").click(function() {
        button.closest("form").submit();
      });
    }
  }
}

So we wanted to test this using Jasmine.
Our main goal was to test that when the button was clicked the form that contains it was being submitted.
It would be cool if we could use Jasmine’s spies:

describe("myApp.buttonBinder", function() {
  it("should bind form submit to button", function() {
    var form = $("<form/>");
    var button = $("<input/>");
    form.append(button);
    spyOn(form, 'submit');

    myApp.buttonBinder.bind(button);
    button.click();

    expect(form.submit).toHaveBeenCalled();
  });
});

But unfortunately this does not work. I didn’t look too much into it, but I suspect it is due to the fact that when the actual code runs, the jQuery ‘closest’ selector creates a new form object that represents the same DOM element. But as it is a different object the spy can’t recognize that the function submit has been called.
One idea is to instead add a listener to the form’s submit event and check if this listener has been called:

describe("myApp.buttonBinder", function() {
  it("should bind form submit to button", function() {
    var form = $("<form/>");
    var button = $("<input/>");
    form.append(button);
    var formHasBeenSubmitted = false;
    form.submit(function() {
      formHasBeenSubmitted = true;
    });

    myApp.buttonBinder.bind(button);
    button.click();

    expect(formHasBeenSubmitted).toBeTruthy();
  });
});

And voilá! This works!
But it looks terrible, doesn’t it?
Good thing Jasmine lets us extend itself, so we can create a new spy function and a new custom matchers:

// SpecHelper.js
var jasmineExtensions = {
  jQuerySpies: {},
  spyOnEvent: function(element, eventName) {
    var control = {
      triggered: false
    };
    element.bind(eventName, function() {
      control.triggered = true;
    });
    jasmineExtensions.jQuerySpies[element[eventName]] = control;
  };
};

var spyOnEvent = jasmineExtensions.spyOnEvent;

beforeEach(function() {
  this.addMatchers({
    toHaveBeenTriggered: function() {
      var control = jasmineExtensions.jQuerySpies[this.actual];
      return control.triggered;
    }
  });
});

And now our test looks like this:

describe("myApp.buttonBinder", function() {
  it("should bind form submit to button", function() {
    var form = $("<form/>");
    var button = $("<input/>");
    form.append(button);
    spyOnEvent(form, 'submit');

    myApp.buttonBinder.bind(button);
    button.click();

    expect(form.submit).toHaveBeenTriggered();
  });
});

Much better \o/

About these ads
  1. David
    May 17, 2011 at 22:01 | #1

    Line 06 of your original spec should read:

    spyOn(form, ‘submit’).andReturn(false);

  2. Anonymous
    March 19, 2012 at 21:55 | #2

    Hi!

    I’ve just uploaded a Gist with some improvements of your helper at https://gist.github.com/2123671

  3. January 24, 2013 at 12:49 | #3

    What exactly genuinely stimulated u to publish “Testing events on jQuery objects with
    Jasmine | Perdido is Lost!”? I reallyseriously
    adored the blog post! Thanks for your time ,Charline

  4. February 2, 2013 at 06:15 | #4

    Hello! I could have sworn I’ve been to this site before but after checking through some of the post I realized it’s new to me.
    Anyways, I’m definitely glad I found it and I’ll be bookmarking and checking back
    often!

  5. February 3, 2013 at 03:49 | #5

    I know this if off topic but I’m looking into starting my own weblog and was wondering what all is required to get setup? I’m assuming
    having a blog like yours would cost a pretty penny?
    I’m not very web savvy so I’m not 100% sure. Any tips or advice would be greatly appreciated. Thank you

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: