How to Verify Jquery Ajax Events with Jasmine

How do I use jasmine-ajax to verify that the send method was called?

Yes you are not calling ajax.send(), but you are triggering the ajax.onreadystatechange event because of this piece of code:

jasmine.Ajax.requests.mostRecent().respondWith({
"status": 200,
"contentType": 'application/json',
"responseText": '[1, 2, 4, 3, 5]'
});

Which changes the readystate and sets the readystate to done. This is actually exactly as the documentation also states: https://jasmine.github.io/2.6/ajax.html

As for how to check if xhr.send is actually being called, this SO answer explains you can spy on it doing the following in your beforeEach:

spyOn(XMLHttpRequest.prototype, 'send');

After uncommenting the xhr.send() part in your loader you could check for method calls like this:

describe("#loadNames", function () {    
it("Makes a success callback with the data when successful", function () {
Loader.loadNames("someURL", successFunction, failFunction);
jasmine.Ajax.requests.mostRecent().respondWith({
"status": 200,
"contentType": 'application/json',
"responseText": '[1, 2, 4, 3, 5]'
});

expect(XMLHttpRequest.prototype.open).toHaveBeenCalled();
});
});

How do I test a function that calls an AJAX function without firing it using jasmine?

I suggest mocking out the call rather than actually calling it. The granularity is up to you, you can either stub the ajax call, or stub the whole submit function.

Here is how you can stub the submit function:

spyOn(controller, 'submit').and.callFake(function() {
DataService.ticketNumber = somevalue;
});

Place that code prior to the actually call to the controller which caller.clickSubmit().

You can then follow up with expectations on the spy, such as:

expect(controller.submit).toHaveBeenCalled()

Or any of the other expectations related to spyOn.

Here are the jasmine docs: http://jasmine.github.io/2.0/introduction.html

Look down in the 'spies' area.

If you want to mock up the ajax call, you will have to mock a promise like this:

spyOn($, 'ajax').and.callFake(function() {
var deferred = $q.defer();
deferred.resolve(someResponse);
return deferred.promise;
});

Also, in order to get the code waiting on the promise to resolve, after the submit call has been made, you need to run a $scope.$digest() so that angular can handle the promise resolution. Then you can check your expectations on code that depended on the resolution or rejection of the promise.

Testing a function that is called from an jQuery AJAX callback with Jasmine

This all boils down to how you spy on your objects and writing code that is more testable. Let's work through a few strategies.

Strategy 1

Given your current code is not within an object, you could test that these functions are called by simply testing their implementation directly.

Instead of testing that the functions were called, you would test their implementation directly.

Example

describe("strategy 1", function () {
var ajaxSpy;

beforeEach(function () {
ajaxSpy = spyOn($, 'ajax');

ajaxCall();
});

describe("error callback", function () {
beforeEach(function() {
spyOn(window, 'alert');

var settings = ajaxSpy.calls.mostRecent().args[0];
settings.error();
});

describe("when there is an error", function() {
it("should alert an error message", function() {
expect(window.alert).toHaveBeenCalledWith('Error');
});
});
});
});

Strategy 2

While the above works, it can be cumbersome to write tests. Ideally, you want to test the invocation and implementation separately.

To do so, we can spy on these functions. Since these are in the global namespace, you can spy on them through the window object.

Example

describe("strategy 2", function () {
var ajaxSpy;

beforeEach(function () {
ajaxSpy = spyOn($, 'ajax');

ajaxCall();
});

describe("error callback", function () {
beforeEach(function() {
spyOn(window, 'displayError');

var settings = ajaxSpy.calls.mostRecent().args[0];
settings.error();
});

describe("when there is an error", function() {
it("should alert an error message", function() {
expect(window.displayError).toHaveBeenCalled();
});
});
});
});

Strategy 3 (Recommended)

The final strategy, and what I recommend, has a similar setup to the second strategy, except we encapsulate our implementation into a custom object.

Doing so makes the code more testable by wrapping functionality in objects and avoids the global namespace (i.e. window).

Example

describe("solution 3", function() {
var ajaxSpy;

beforeEach(function() {
ajaxSpy = spyOn($, 'ajax');

ajaxService.ajaxCall();
});

describe("error callback", function() {
beforeEach(function() {
spyOn(ajaxService, 'displayError');

var settings = ajaxSpy.calls.mostRecent().args[0];
settings.error();
});

it("should alert an error message", function() {
expect(ajaxService.displayError).toHaveBeenCalled();
});
});
});

Jasmine test values returned by Ajax during success

Try adding a callback function to your original AJAX call, then use Jasmine's asynchronous support to test it when the call has completed.

myFunction(done) {
$.ajax(){
method: "GET"
url: "/myTest"
success: function(test){
$("#htmlElem").html(test);
done();
}
};
};

it("specifying response when you need it", function(done) {
// ...
myFunction(function() {
var testVal = $("htmlElem").val();
expect(testVal).toBe('awesome response');
done();
});
});

Testing results of an initial ajax request with Jasmine 2.0

It doesn't look like you're actually running async specs. I notice your beforeEach and it both receive a done callback, but neither of them call that. And if you're stubbing out ajax, then you probably don't need to be running an async spec, because the stub will
effectively make the ajax request be synchronous.

When I run the following code, everything works fine.

Spec:

describe('View', function() {

var formFieldValues = [{name: 'foo'}, {name: 'bar'}];
var view;

beforeEach(function() {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest('/form-fields').andReturn({
responseText: JSON.stringify(formFieldValues)
});
view = new View(); // makes ajax call on initialization
});

afterEach(function() {
jasmine.Ajax.uninstall();
});

it('form fields have values', function() {
// PROBLEM IS HERE: THE FOLLOWING RUNS BEFORE VIEW'S AJAX CALL FINISHES
expect(view.$('[name="fieldName"]').children().length).toEqual(formFieldValues.length);
});

});

My dummy implementation:

Collection = Backbone.Collection.extend({
url: '/form-fields'
});

View = Backbone.View.extend({
initialize: function() {
this.collection = new Collection();
this.collection.on('reset', this.render, this);
this.collection.fetch({reset: true});
},

render: function() {
var innerEl = $('<div name="fieldName"></div>')
this.collection.each(function(item) {
innerEl.append('<span></span>');
});
this.$el.append(innerEl);
}
});

Jasmine testing ajax request after click()

Thanks to Andreas Koberle's answer I got it working.

Here is the code:

window.addEvent = ->
$("#save-search").click ->
# Do stuff like define value here...

$.ajax
type: "POST"
url: "/your/url/here"
data:
key: value

dataType: "json"
success: (msg) ->
alert "Your message here"

false

jQuery -> addEvent()

Here is the test:

setUpHTMLFixture = ->
loadFixtures "your_fixture"

describe "save_search", ->
beforeEach ->
setUpHTMLFixture()

it "should call ajax with your url", ->
addEvent()
spyOn jQuery, "ajax"
$("#save-search").click()
expect($.ajax.mostRecentCall.args[0]["url"]).toEqual("/your/url/here")

The fixture: your_fixture.html

<a id="save-search">Save Search</a>

<div class="ui-draggable">some stuff</div>

<div class="ui-draggable">some stuff</div>


Related Topics



Leave a reply



Submit