问题
I am using Jasmine 2.5.2 to write unit tests for code that performs Ajax requests using jQuery 3.1.1 . I would like to mock out the Ajax call, providing my own response status and text.
I am using the Jasmine ajax plug-in (https://github.com/pivotal/jasmine-ajax).
Following the example on https://jasmine.github.io/2.0/ajax.html, which uses the XMLHttpRequest object, works fine.
describe("mocking ajax", function() {
describe("suite wide usage", function() {
beforeEach(function() {
jasmine.Ajax.install();
});
afterEach(function() {
jasmine.Ajax.uninstall();
});
it("specifying response when you need it", function() {
var doneFn = jasmine.createSpy("success");
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(args) {
if (this.readyState == this.DONE) {
doneFn(this.responseText);
}
};
xhr.open("GET", "/some/cool/url");
xhr.send();
expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url');
expect(doneFn).not.toHaveBeenCalled();
jasmine.Ajax.requests.mostRecent().respondWith({
"status": 200,
"contentType": 'text/plain',
"responseText": 'awesome response'
});
expect(doneFn).toHaveBeenCalledWith('awesome response');
});
});
});
NB: this differs slightly from the documented example, had to change jasmine.Ajax.requests.mostRecent().response()
to jasmine.Ajax.requests.mostRecent().respondWith()
.
When I use jQuery Ajax, the doneFn is never called.
describe("mocking ajax", function() {
describe("suite wide usage", function() {
beforeEach(function() {
jasmine.Ajax.install();
});
afterEach(function() {
jasmine.Ajax.uninstall();
});
it("specifying response when you need it", function() {
var doneFn = jasmine.createSpy("success");
$.ajax({
method: "GET",
url: "/some/cool/url"})
.done(function(result) {
doneFn(result);
});
expect(doneFn).toHaveBeenCalledWith('awesome response');
});
});
});
Jasmine states that
Jasmine-Ajax mocks out your request at the XMLHttpRequest object, so should be compatible with other libraries that do ajax requests.
$.ajax returns a jqXHR from 1.4.x and not XMLHttpRequest - does this break support with Jasmine Ajax?
回答1:
Here are a couple of ways you could mock ajax in your jasmine tests
Approach A:
- Using mock ajax.js, you can mock the global xhr object as described in the doc
- However you'll need to specify a spy on the success function and let it callThrough (as seen in the code below)
See it in action here
describe('ajax test suite', function() { beforeEach(function() { jasmine.Ajax.install(); }); afterEach(function() { jasmine.Ajax.uninstall(); }); it('sample test', function() { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(args) { if (this.readyState == this.DONE) { testObj.successFunction(this.responseText); } }; spyOn(testObj, 'successFunction').and.callThrough(); xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1"); xhr.send(); expect(jasmine.Ajax.requests.mostRecent().url).toBe('https://jsonplaceholder.typicode.com/posts/1'); expect(testObj.successFunction).not.toHaveBeenCalled(); jasmine.Ajax.requests.mostRecent().respondWith({ "status": 200, "contentType": 'text/plain', "responseText": 'awesome response' }); expect(testObj.successFunction).toHaveBeenCalledWith('awesome response'); }); });
Approach B:
Directly mocking the $.ajax object. Be it get/post/load or any ajax flavor from jquery, you can simply mock the $.ajax as shown below.
var testObj = { ajaxFunction : function(url){ $.ajax({url : url}).done(this.successFunction.bind(this)); }, successFunction : function(data){ console.log(data); } } describe('ajax test suite', function(){ it('sample test', function(){ testObj.ajaxFunction('https://jsonplaceholder.typicode.com/posts/1'); spyOn($, 'ajax').and.callFake(function(e) { return $.Deferred().resolve({'text':'this a a fake response'}).promise(); }); spyOn(testObj, 'successFunction').and.callThrough(); testObj.ajaxFunction('https://jsonplaceholder.typicode.com/posts/1'); expect(testObj.successFunction).toHaveBeenCalledWith({'text':'this a a fake response'}); }); });
回答2:
When mocking the $.ajax object, the parameter passed in can be used directly to trigger a success or failure, without going through the promise interface.
spyOn($, 'ajax').and.callFake(function (options) {
var result = undefined, status, xhr;
options.success(result, status, xhr);
});
spyOn($, 'ajax').and.callFake(function (options) {
var xhr = undefined, status, error;
options.error(xhr, status, error);
});
来源:https://stackoverflow.com/questions/41851692/mocking-jquery-ajax-calls-with-jasmine