A function that registers a ready-made handler must register another function, not an anonymous code block. You can then test the code that calls $ .ready () separately from the code that runs when ready. So you have:
- One test to verify the correct function is set as a ready-made handler
- Another test to test the finished handler makes the right stuff
To test scenario 1, you need to introduce a test double for jQuery. This is tricky, as if you redefined $ or jQuery, the likelihood that you would ruin another code that relies on it for other processing (like a test runner). At the same time, your code may still want to directly call jQuery when it uses utility methods such as array concatenation. Any management call template should address this, though ( http://martinfowler.com/articles/injection.html ).
Anyway, some code is used here using the constructor installation (using JSMock for a mock library and QUnit (jQuery) for a 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 generic template for all event handlers in JS ... You might want to use prototype type classes. When you pass functions as jQuery parameters, you need to know that the "this" value will not be set by jQuery when these callbacks are called. In the test, this is interrupted because equals (callback, sut.OnReady) no longer passes. To solve this problem, you need to make event handlers direct members of each instance. You can imagine when there is a series then good to have a utility that takes their list, but this demonstrates the creation of an βOnReadyβ member that 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() { }
source share