Mocking XMLHttpRequests in Jasmine
Posted on Mon 19 October 2015 in javascript
When writing integration tests, it's often useful to fake network requests so your tests can be predictable and execute quickly. Jasmine provides jasmine-ajax
which will replace the XMLHttpRequest
constructor on the global (eg: window
).
Wiring up jasmine.Ajax into your test-suites' lifecycle is pretty straight forward:
import 'jasmine-ajax';
describe('myTestSuite', () => {
beforeEach(() => jasmine.Ajax.install());
afterEach(() => jasmine.Ajax.uninstall());
});
As per the docs, you can then pre-program responses before your subject under test makes them:
jasmine.Ajax.stubRequest('http://example.org/api')
.andReturn({ responseText: JSON.stringify({ json: 'ok!' }) });
jasmine-ajax executes all XHR callbacks on the same tick making them effectivly syncronous. This results in your test-code much easier to write and reason about.
Pairing with xhr
XMLHttpRequest
doesn't have the nicest API to work with; fortunatley the xhr
module on NPM provides a nice, lightweight abstraction (it even smooths out the cracks in IE8). However, it's not all plain sailing as the xhr module caches the XMLHttpRequest
object on the window when it is loaded (ie: require()
'd).
module.exports.XMLHttpRequest = window.XMLHttpRequest;
This results in requests made using the xhr
module ignorning the fake XMLHttpRequest
constructor that jasmine-ajax
has placed on the window. Fortunatley there's an easy fix, just re-assign xhr.XMLHttpRequest
after you've installed jasmine-ajax:
import 'jasmine-ajax';
import xhr from 'xhr';
describe('myTestSuite', () => {
beforeEach(() => {
jasmine.Ajax.install();
xhr.XMLHttpRequest = window.XMLHttpRequest;
});
afterEach(() => {
jasmine.Ajax.uninstall();
xhr.XMLHttpRequest = window.XMLHttpRequest;
});
});
Closing thoughts on Jasmine
I've recently switched to Jasmine after using Mocha and SinonJS succesfully for a few years. My hope was that the Jasmine ecosystem, which has a lot of funtionality built-in, would be more productive and easier for new-comers to the project to pick up than Mocha. I've been very impressed by how easy it was to get up and running on Jasmine; and the v2.3 API is pleasant to code against (it will be instantly familiar to chai users), however I do wish that Jasmine made it easier to write custom matchers as I find myself missing the verbosity of SinonJS's sinon.match(val => { ... })
.