axios: Test requests by stubbing

Kier Borromeo
3 min readFeb 26, 2016

--

What I love about http libraries like axios is not just how it makes it simple to make a request, but also how simple it is to test it.

Normally, you would run a fake server (with a library like Sinon), and imitate responses to test a request. After doing this myself for a bazillion times, I came up with the idea of stubbing axios methods. I’ll be showing an example shortly.

Here’s an example written in ES2015:

ES2015 is a significant update to the language, and the first major update to the language since ES5 was standardized in 2009.

import axios from 'axios';
import $ from 'jquery';
const $users = $(‘#users’);export default function getUsers {
return axios.get(‘http://api.yolo.com/users’)
.then((res) => {
if ( res.data.length ) {
$users.innerHTML = ‘The list is empty’;
} else {
$users.innerHTML = res.data.join(',');
}
})
.catch((res) => {
$users.innerHTML = 'An error occured.'//;
return Promise.reject(res);
});
}

get-users.js Basically, all this does is request to http://api.yolo.com/users. 3 things may happen: a.) “The list is empty” is rendered. b.) The data is rendered, joined by a comma. c.) “An error occured.” is rendered.

import {expect} from 'chai';
import sinon from 'sinon';
import getUsers from ‘./get-users’;
describe(‘get-users’, () => {
let sandbox;
let server;
beforeEach(() => {
sandbox = sinon.sandbox.create();
server = sandbox.useFakeServer();
});
afterEach(() => {
server.restore();
sandbox.restore();
});
it(‘should display a blankslate’, (done) => {
getUsers()
.then(() => {
expect($(‘#users’).innerHTML)
.to.equal(‘The list is empty’) })
.then(done, done);
setTimeout(() => server.respond([200,
{ ‘Content-Type’: ‘application/json’ },
‘[]’]), 0);
});
});

get-users.spec.jsIn the test file above, we’re imitating a sinon sandbox so we can easily restore the objects we stubbed. Afterwards, we’re initiating a fake server. The test case simply creates a request listener (server.respondWith), runs the function we’re testing, and makes some assertions. We’re using setTimeout because Sinon responds after 10ms.

Looks pretty simple, right? Let’s add a few more tests!

import {expect} from 'chai';
import sinon from 'sinon';
import getUsers from ‘./get-users’;
describe(‘get-users’, () => {
let sandbox;
let server;
beforeEach(() => {
sandbox = sinon.sandbox.create();
server = sandbox.useFakeServer();
});
afterEach(() => {
server.restore();
sandbox.restore();
});
it(‘should display a blankslate’, (done) => {
getUsers()
.then(() => {
expect($(‘#users’).innerHTML)
.to.equal(‘The list is empty.’) })
.then(done, done);
setTimeout(() => server.respond([200,
{ ‘Content-Type’: ‘application/json’ },
‘[]’]), 0);
});
it(‘should display the data’, (done) => {
getUsers()
.then(() => {
expect($(‘#users’).innerHTML)
.to.equal(‘john,doe,pogi’) })
.then(done, done);
setTimeout(() => server.respond([200,
{ ‘Content-Type’: ‘application/json’ },
JSON.stringify(['john','doe','pogi']), 0);
});
it(‘should display the data’, (done) => {
getUsers()
.catch(() => {
expect($(‘#users’).innerHTML)
.to.equal(‘An error occured.’) })
.then(done, done);
setTimeout(() => server.respond([500,
{ ‘Content-Type’: ‘application/json’ },
'{}']), 0);
});

});

It does still look OK. But just how convoluted can this get in more complicated cases?

Here’s an example with just stubbing axios methods.

import {expect} from 'chai';
import axios from ‘axios’;
import sinon from ‘sinon’;
import getUsers from ‘./get-users’;
describe(‘get-users’, () => {
let sandbox;
beforeEach(() => sandbox = sinon.sandbox.create());
afterEach(() => sandbox.restore());
it(‘should display a blankslate’, (done) => {
const resolved = new Promise((r) => r({ data: [] }));
sandbox.stub(axios, ‘get’).returns(resolved);
getUsers()
.then(() => {
expect($(‘#users’).innerHTML)
.to.equal(‘The list is empty.’) })
.then(done, done);
});
it(‘should display the data’, (done) => {
const data = [‘john’, ‘doe’, ‘pogi’];
const resolved = new Promise((r) => r({ data }));
sandbox.stub(axios, ‘get’).returns(resolved);
getUsers()
.then(() => {
expect($(‘#users’).innerHTML)
.to.equal(‘john,doe,pogi’) })
.then(done, done);
});
it(‘should display the data’, (done) => {
const rejected = new Promise((_, r) => r());
sandbox.stub(axios, ‘get’).returns(rejected);
getUsers()
.catch(() => {
expect($(‘#users’).innerHTML)
.to.equal(‘An error occured.’) })
.then(done, done);
});
});

In the file above, we’re stubbing the axios method we used (axios.get in this case) to return a fulfilled/rejected promise depending on the test. We’re no longer using a fake server to respond to our request.

It’s not significantly shorter, but I find it to be more direct. To add, there were some cases where I would forget Sinon’s Fake Server API in which I would have to go to the trouble of checking the docs.

This works for most cases, except for cases like testing whether the Authorization header was added to the request, and so on.

Take this with a grain of salt. I have no concrete idea whether this is a good practice or not.

Update: axios author, Matt Zabriskie, recently published moxios, a helper to mock axios requests for testing. It provides a high level way of testing axios requests without having to manually stub them. Don’t forget to check it out!

--

--