Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I simulate a timeout event in a XHR using jasmine?

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)
like image 778
Pablo Lozano Avatar asked Oct 20 '22 11:10

Pablo Lozano


1 Answers

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

like image 128
Michael Radionov Avatar answered Oct 22 '22 00:10

Michael Radionov