How to properly unit test jQuery's .ajax() promises using Jasmine and/or Sinon?

I've got a fairly straightforward function which returns a jQuery .ajax() promise as such:

CLAW.controls.validateLocation = function(val, $inputEl) {
    return $.ajax({
        url: locationServiceUrl + 'ValidateLocation/',
        data: {
            'locationName': val
        },
        beforeSend: function() {
            $inputEl.addClass('busy');
        }
    }).done(function(result) {
        // some success clauses
    }).fail(function(result) {
        // some failure clauses
    }).always(function() {
        // some always clauses
    });
}

For the most part, this new promises interface works like a dream, and eliminating callback pyramids when using jQuery's .ajax() is great. However, I cannot for the life of me figure out how to properly test these promises using Jasmine and/or Sinon:

  1. All of Sinon's documentation assumes you're using old-school callbacks; I don't see a single example of how to use it with promises/deferreds

  2. When attempting to use a Jasmine or Sinon spy to spy on $.ajax, the spy is effectively overwriting the promise, so its done, fail, and always clauses no longer exist on the ajax function, so the promise never resolves and tosses an error instead

I'd really just love an example or two of how to test these new jQuery .ajax() promises with the aforementioned testing libs. I've scoured the 'net fairly intensely and haven't really dredged up anything on doing so. The one resource I did find mentioned using Jasmine.ajax, but I'd like to avoid that if possible, seeing as Sinon provides most of the same capabilities out-of-the-box.


Solution 1:

It is not that complex actually. It suffices to return a promise and resolve it according to your case.

For example:

spyOn($, 'ajax').andCallFake(function (req) {
    var d = $.Deferred();
    d.resolve(data_you_expect);
    return d.promise();
});

for a success, or

spyOn($, 'ajax').andCallFake(function (req) {
    var d = $.Deferred();
    d.reject(fail_result);
    return d.promise();
});

for a failure.

For Jasmine 2.0 the syntax has changed slightly:

spyOn($, 'ajax').and.callFake(function (req) {});

the method .andCallFake() does not exist in Jasmine 2.0

Solution 2:

something along these lines / with sinon and jQuery deferreds

ajaxStub = sinon.stub($, "ajax");

function okResponse() {
  var d = $.Deferred();
  d.resolve( { username: "testuser", userid: "userid", success: true } );
  return d.promise();
};

function errorResponse() {
 var d = $.Deferred();
 d.reject({},{},"could not complete");
 return d.promise();
};

ajaxStub.returns(okResponse());
ajaxStub.returns(errorResponse());