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/
Line 06 of your original spec should read:
spyOn(form, ‘submit’).andReturn(false);
Hi!
I’ve just uploaded a Gist with some improvements of your helper at https://gist.github.com/2123671
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
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!
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
I Think post, “Testing events on jQuery objects with Jasmine
| Perdido is Lost!” Hunter Douglas was great!
I personallycannot see eye to eye along with u more!
Finally appears like I personallylocated a weblog well worth checking out.
Thanks, Lawanna