I'm testing a function that makes AJAX requests, allowing retries when the network is not working or there are timeouts because the connection is unstable (I'm thinking about mobile devices).
I'm sure it works because I've used it integrated with other code, but I want to have a proper test.
However I haven't been able to create a unit test to ensure it formally. I'm using jasmine 2.3 along with karma and here is my code so far:
var RETRIES=2;
var TIMEOUT=1000;
function doRequest(method, url, successFn, errorFn, body, retries) {
var request = new XMLHttpRequest();
retries = retries === undefined ? RETRIES : retries;
request.open(method, url);
request.setRequestHeader('Accept', 'application/json');
request.setRequestHeader('Content-Type', 'application/json');
request.onload = function () {
if (request.status < 300 && request.status >= 200) {
successFn(JSON.parse(request.responseText));
} else {
if (errorFn) {
errorFn(this.status, this.responseText);
}
}
};
request.onerror = function () {
if (this.readyState === 4 && this.status === 0) {
//there is no connection
errorFn('NO_NETWORK');
} else {
errorFn(this.status, this.responseText);
}
};
if (retries > 0) {
request.ontimeout = function () {
doRequest(method, url, successFn, errorFn, body, retries - 1);
};
} else {
request.ontimeout = function () {
errorFn('timeout');
};
}
request.timeout = TIMEOUT;
if (body) {
request.send(body);
} else {
request.send();
}
}
And this is my test:
describe('Ajax request', function () {
'use strict';
var RETRIES=2;
var TIMEOUT=1000;
beforeEach(function () {
jasmine.Ajax.install();
jasmine.clock().install();
});
afterEach(function () {
jasmine.Ajax.uninstall();
jasmine.clock().uninstall();
});
it(' should call error callback function when a tiemout happens', function () {
var doneFn = jasmine.createSpy('onLoad');
var errorFn=jasmine.createSpy('onTimeout');
doRequest('GET','http://www.mockedaddress.com', doneFn, errorFn,null,RETRIES);
var request = jasmine.Ajax.requests.mostRecent();
expect(request.method).toBe('GET');
jasmine.clock().tick(TIMEOUT*(RETRIES+1)+50); //first attempt and then 2 retries
expect(errorFn).toHaveBeenCalled(); // assertion failed
});
});
And this is the output of the test:
Expected spy onTimeout to have been called.
at Object.<anonymous> (js/test/doRequest_test.js:75:0)
Chrome 42.0.2311 (Windows 7): Executed 1 of 1 (1 FAILED) ERROR (0.007 secs / 0.008 secs)
Use .responseTimeout
on a mocked request object, this method is provided by jasmine-ajax, but is not documented.
jasmine.Ajax.requests.mostRecent().responseTimeout();
But after that, calling .responseTimeout
on the first request, you still have an error, because the errorFn
callback fires only after several retries. You have to deal with the next requests which are being sent automatically by request.ontimeout
defined in your code. It can be solved like that:
var retries = 3;
var request;
do {
request = jasmine.Ajax.requests.mostRecent();
request.responseTimeout();
retries -= 1;
} while(retries >= 0);
As a result, it will call a timeout
for each subsequent request.
Here is some sandbox to play with the code http://plnkr.co/edit/vD2N66EwkpgiLFpg1Afc?p=preview
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With