The function that registers the on ready handler should register another function, not an anonymous codeblock. Then you can test the code that calls $.ready() separate from the code that runs on ready. So you have:
- One test to verify the right function is set as the the ready handler
- Another test to verify the ready handler does the right stuff
To test scenario 1, you'll need to inject a test double for jQuery. This is difficult as if you redefine $ or jQuery, odds are you'll screw up other code that relies on it for other processing (like the test runner). At the same time your code may still want to call jQuery directly when its using utility methods like array concatenation. Any inversion-of-control pattern should address this though (http://martinfowler.com/articles/injection.html).
Anyhow, here's some code using constructor injection (using JSMock for the mocking library, and QUnit (of jQuery) for the test runner):
// the code
var createComponent = function(_$) {
var that = {};
that.OnStart = function() {
_$.ready(this.OnReady);
};
that.OnReady = function() {
};
return that;
};
// the test
test("OnStart associates the ready handler", function() {
var sut;
var mock$ = mc.createMock($);
mock$.expects().ready(isA.TypeOf(Function)).andStub(function(callback) {
equals(callback, sut.OnReady);
});
sut = createComponent(mock$);
sut.OnStart();
mc.verify();
});
test("OnReady does the right stuff", function() {
//etc
});
I use this general pattern for all event handlers in JS... You might prefer to use prototype type classes. When you pass functions as parameters to jQuery, you need to be aware that the "this" value will not be set by jQuery when those callbacks are called. In the test, this breaks because equals(callback, sut.OnReady) no longer passes. To address this, you need to make the event handlers direct members of each instance. You can imagine when there are a number of then its nice to have a util that takes a list of them, but this demonstrates making 'OnReady' a member who does not rely on 'this'.
var Component = function(_$) {
this._$ = _$;
// repeat for each event handler thats tested
this.OnReady = function() {
Component.prototype.OnReady.apply(this);
}
}
Component.prototype.Start = function() {
this._$.ready(this.OnReady);
}
Component.prototype.OnReady = function() {
}