(2 minute read)
In writing UI tests for my new web app I hit a snag today. I have a notification system which uses jQuery's fadeIn() and fadeOut() methods to control how notification messages get displayed to the user. And I want to test that these methods are being called with right parameters and in the right order. So I should just be able to sinon.spy() them, right? Not quite.
As detailed in this StackOverflow post, when you use $() you create a new instance of the jQuery object. Plus the effect methods noted above aren't accessible as part of a static API exposed by jQuery. So the only way to mock them would be to either:
I opted for the second approach as it seemed more elegant - being able to use the jQuery methods directly whilst transparently recording the method call history. Here is the code to do this for the fadeIn() method:
(function($) {
var self = this;
// the array where method call history is stored
self.jQueryMethodHistory = [];
// helper method used to insert new data into the history array
var process = function(methodName, arguments) {
self.jQueryMethodHistory.push({
name: methodName,
args: Array.prototype.slice.call(arguments, 0) // convert arguments into a real array
})
};
// list of methods we want to track
var methodsToTrack = [
'fadeIn'
];
for (var i=0; i<methodsToTrack.length; ++i) {
(function(methodName) {
// we override the method
$.fn[methodName] = (function() {
var orig = $.fn[methodName];
return function() {
// track the call
process(this, methodName, arguments);
// transparently send the call through to the original method
orig.apply(this, arguments);
}
}());
})(methodsToTrack[i]);
}
})(jQuery);
As you can see it's quite simple. We store all method calls into a global array - jQueryMethodHistory - which we can clear and manipulate at any time. All calls to fadeIn() get sent to the actual method but whilst also being recorded (with method arguments) into the history array. And if we wanted to we could pass in the list of methods to be tracked in this way at runtime - the use of the methodsToTrack array hints at this.
NOTE: fadeIn() internally calls show() once it's done. Likewise, fadeOut() internally calls hide() once done.