Sinon: Correct usage of sinon's fakeXMLHttpRequest in node.js

Created on 1 Sep 2015  ·  7Comments  ·  Source: sinonjs/sinon

I am creating XMLHttpRequest javascript module to get JSON data from server. Here is the code:

(function() {
  var makeRequest = function(url,callback,opt) {
    var xhr;
    if (XMLHttpRequest) { // Mozilla, Safari, ...
      xhr = new XMLHttpRequest();
    } else if (ActiveXObject) { // IE
      try {
        xhr = new ActiveXObject("Msxml2.XMLHTTP");
      }
      catch (e) {
        try {
          xhr = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch (e) {}
      }
    }

    if (!xhr) {
      callback.call(this,
        'Giving up :( Cannot create an XMLHTTP instance',
        null);
      return false;
    }
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          var data = xhr.responseText;
          if(opt && !opt.raw) {
            try {
              data = JSON.parse(data);
            } catch (e) {
              callback.call(this, e,null);
              return;
            }
          }
          callback.call(this,null,data);
        } else {
          callback.call(this,
            'There was a problem with the request.',
            null);
        }
      }
    };
    var params = '';
    if (opt && opt.params && typeof(opt.params) == 'object') {
      for( var key in opt.params) {
        params += encodeURIComponent(opt.params[key]);
      }
    }
    var method = opt && opt.method ? opt.method : 'GET';
    if (method == 'GET') {
      url = params.length > 0 ? url+'?'+params : url;
      xhr.open('GET', url);
      xhr.send();
    } else if (method == 'POST') {
      var data = opt && opt.data ? opt.data : params;
      xhr.open('POST', url);
      xhr.send(JSON.stringify(data));
    }
    return xhr;
  }

  if(typeof module !== 'undefined' && module.exports) {
    module.exports = makeRequest;
  }
  if(typeof window!== 'undefined') {
    window.getJSONData = makeRequest;
  }
})();

I am writing the test case for this on nodejs with Mocha and Sinon. Using Sinon's fakeXMLHttpRequest to test the module. Here is the test code:

var expect = require('chai').expect,
  getJSON = require('../'),
  sinon = require('sinon');

describe('get-json-data test the request', function() {
  beforeEach(function() {
    this.xhr = sinon.useFakeXMLHttpRequest();
    var requests = this.requests = [];

    this.xhr.onCreate = function (xhr) {
      requests.push(xhr);
    };
  });
  afterEach(function() {
    this.xhr.restore();
  });

  it('get json data', function() {
    var callback = sinon.spy();
    getJSON('/some/json', callback);
    expect(this.requests.length).to.equal(1);
    this.requests[0].respond(200,
      {"Content-Type": "application/json"},
      '{"id": 1, "name": "foo"}');
    sinon.assert.calledWith(callback, {"id": 1, "name": "foo"});
  });
});

On running the test I get error:

ReferenceError: XMLHttpRequest is not defined

And it seems correct as there is no XMLHttpRequest class/function in nodejs. But is Sinon's fakeXMLHttpRequest not supposed to do that. I thought in Sinon's setUp (Mocha's beforeEach) we are creating a fake XMLHttpRequest with fakeXMLHttpRequest. Please suggest what I am doing wrong? Or what would be the correct way to use Sinon to test my module at nodejs?

Most helpful comment

For those who would like to test Ajax in node using just mocha and sinon, you just have to set global.XMLHttpRequest to sinon.useFakeXMLHttpRequest(). Here is an example: https://github.com/scriptare/compago-ajax/blob/master/tests/unit.test.js#L36

All 7 comments

There is no "correct" way to test a module that is using DOM APIs using Node, which has no notion of DOM. But there are ways of achieving your goal. The best way to get going is probably to set up Karma to run your tests in PhantomJS or desktop browsers. That would test your code in a browser - the actual environment where it will be used!

If you do insist on testing the code _in Node_, then you could try out jsdom, which will add the DOM API to the current context. There are mocha plugins, so you could do something like this

var jsdom = require('mocha-jsdom')
var expect = require('chai').expect

describe('mocha tests', function () {

  jsdom()

  it('has document', function () {
    var div = document.createElement('div')
    expect(div.nodeName).eql('DIV')
  })

})

Seems there are others using jsdom and sinon.js, so you should not have too many problems.

Thanks @fatso83 So I'll try to implement Karma with PhantomJS.

@gagan-bansal if you need some setup code to get going you can get a start here

@fatso83 thanks for the reference I'll check it.

For those who would like to test Ajax in node using just mocha and sinon, you just have to set global.XMLHttpRequest to sinon.useFakeXMLHttpRequest(). Here is an example: https://github.com/scriptare/compago-ajax/blob/master/tests/unit.test.js#L36

@zandaqo thanks for update.

Was this page helpful?
0 / 5 - 0 ratings