About

What is JQuery Spy?

Jquery spy is a plugin to extend jquery, qunit and sinon.js providing developers a concise, elegant way to write / describe tests for test driven development (tdd)

Why do we need JQuery Spy?

The most important reason is to be able to test ajax in a ledgible way.
Secondly, to encourage testing by making it easier for every tom dick and harry javascript developer.

As a Developer, I want to test

As a Developer, I want to test the following in an easy to remember syntax:

  1. Test the $.ajax method was called
  2. Test ajax request url
  3. Test ajax request type
  4. Test ajax request dataType
  5. Test ajax response success method was called within a certain time
  6. After ajax response, test an element exists
  7. After ajax response, test an element style
  8. After ajax response, test an element contents

Without JQuery Spy Example

test( "Test ajax and view", function () { // Prepare spy tests.... var intMaxAjaxWait = 4, intExpectCount = 8, fnTestRequest = function(spyDetails){ ok($.ajax.calledOnce, 'ajax has been called '); var ajaxParams = spyDetails.args[0]; // Run ajax request tests equals( ajaxParams.url, "/actions/jsonpproxy/?url=/actions/json.js" , 'ajax url'); equals( ajaxParams.dataType, "jsonp", 'ajax jsonp' ); equals( ajaxParams.type, "GET", 'ajax type' ); }, fnTestResponse = function(spyDetails){ // Collect data from spy and run response tests ok(spyDetails, 'ajax success method completed under ' + intMaxAjaxWait + ' seconds.'); ok( $('#someSelector').length, 'someSelector exists'); equals( $('#someSelector').html(), 'Some code here...' , 'someSelector html'); equals( $('#someSelector').css('visibility'), 'visible', 'someSelector visibility' ); }; // Use a timer so that we can test if the ajax response returned after a certain time period. // The alternative, which is cleaner is using a $(body).ajaxOnComplete() but this will only be called if we actually change the Dom // Using a timer allows flexibility in testing everything var spyDetails = {returnValue:{status:null}}, intStartTime = new Date().getTime(), fnTimer = function(){ setTimeout(function(){ var intEndTime = new Date().getTime(), intDifference = ((intEndTime - intStartTime) / 1000), isMaxTimeElapsed = intDifference > intMaxAjaxWait; if(spyDetails.returnValue.status === 200){ fnTestResponse(spyDetails); start(); }else if(isMaxTimeElapsed){ fnTestResponse(null); start(); }else{ fnTimer(); } },100); // Test every 10th of a second } expect(intExpectCount); this.spy($, 'ajax'); stop(); fnTimer(); // Run original ajax method $.ajax({ url:'/actions/jsonpproxy/?url=/actions/json.js', dataType:'jsonp', type:'GET', cache:'false', success:function(data){ $('#qunit-fixture').html('<div id="someSelector">Some code here...</div>'); $('#someSelector').css({'visibility':'visible'}); } }); // Collect data from spy and run request tests spyDetails = $.ajax.getCall(0); fnTestRequest(spyDetails); });

With JQuery Spy Example

test( "Test ajax and view", function () { // Prepare spy tests.... var spy = $.spy({ ajax:{ url:"/someUrl/", dataType:'jsonp', type:'GET', success:{ views:[{ el:'#someSelector', css:{'visibility':'visible'}, html:'some code here...' }] } // OR // success:function(){ // var el = $('#someSelector'); // ok(el.length, '#someSelector exists'); // equals(el.css('visibility'), 'visible',' #someSelector is visible'); // equals(el.html(), 'some code here...',' #someSelector contains html'); // } //},config:{ intExpectExtra:3 } } }); // Run original ajax method $.ajax({ url:'/actions/jsonpproxy/?url=/actions/json.js', dataType:'jsonp', type:'GET', cache:'false', success:function(data){ $('#qunit-fixture').html('<div id="someSelector">Some code here...</div>'); $('#someSelector').css({'visibility':'visible'}); } }); // Collect data from spy and run request tests // Collect data from spy and run response tests spy.run(); });