Hi folks, my last two blogs we discussed about how we can use monkey-patching to test complex code logic which could be difficult to test if it is not used, along with that we also looked over how can stub or mock behaviours of our dependencies using sinon, in this blog we would be exploring more utilities provided by sinon i.e; Spies and Stubs.
Spies
In programming we often find methods that returns void and those are often called side effecting methods, which is bad we shall always returns some interesting value from the called method atleast for the APIs that we write, but what about method in different libraries, some of them have plenty of these side effecting methods, so the question comes how to test these methods, well in JavaScript we have a concept of spying a method if it is getting called, let’s see some examples, we would be using sinon utilities for spying the calls.
dependency.js
const add = (x, y) => x + y;
const sub = (x, y) => x >= y ? x - y : y - x;
const callBackDivide = (x, y, callback) => {
return y === 0 ? callback("Can't divide with zero", null) :
callback(null, x / y)
};
const callBackApi = (param, callback) => {
callback(param)
}
module.exports = {
add,
sub,
callBackDivide,
callBackApi
}
stub-spies.js
let dep = require('../stubs/dependency.js');
const sideEffectingMethod = (param) => {
for(let x in param){
dep.callBackApi(param[x], (res) =>{
console.log(`I am side affecting, here is your result ${res}`)
})
}
};
const registerUsers = (userList, callback) => {
for(let user in userList){
callback(user)
}
}
module.exports={
sideEffectingMethod,
registerUsers
}
So we can see from the above two files, that we have a 3rd party library or like in our case file dependency.js
that has a method callBackApi
which is side effecting and we are using that in our api stub-spies.js
and we need to test our method sideEffectingMethod
, in a real world applications the methods like sideEffectingMethod
may include more complex statements but the idea for keeping it simple is to demonstrate the concepts, so now let’s quickly have a look on the spec file that I have written to test it.
stub-and-spies-spec.js
const sinon = require('sinon');
let stubs = require('../blog/spies/stubs-spies.js');
let dep = require('../blog/stubs/dependency.js');
let spiedCallBackApi, mockedCallback;
module.exports = ({it, beforeEach, afterEach, describe}) => {
describe('Spies',() => {
beforeEach(() => {
spiedCallBackApi = sinon.spy(dep,'callBackApi')
});
afterEach(() => {
spiedCallBackApi.restore();
});
it('should spy callBackApi', () => {
stubs.sideEffectingMethod([3]);
spiedCallBackApi.calledWith(3).should.equal(true);
});
it('should spy callBackApi for number of times it is called', () => {
stubs.sideEffectingMethod([3,6,7]);
spiedCallBackApi.firstCall.calledWith(3).should.equal(true);
spiedCallBackApi.secondCall.calledWith(6).should.equal(true);
spiedCallBackApi.thirdCall.calledWith(7).should.equal(true)
})
});
describe('Stubs',() => {
beforeEach(() => {
mockedCallback = sinon.stub()
});
afterEach(() => {
});
it('should able to count number of times callback called', () => {
stubs.registerUsers([2,3,4], mockedCallback);
mockedCallback.calledThrice.should.equal(true);
})
})
};
Now let’s first see the describe block labeled Spies
, we can see in beforeEach we have used sinon
to spy on callBackApi
from method dep
and now with spied object spiedCallBackApi
we can seamlessly do our assertions even we can do assertions with exact number of times or value a method been called, sinon
makes it pretty decent on a spied object sinon provides much more from this.
Stubs
Now let’s look at stubs
, so technical definition put it something like this
A stub is a small program routine that substitutes for a longer program, possibly to be loaded later or that is located remotely.
As we know, in JavaScript we often have to deal with complex callback APIs obviously promise and async/await have made thing simpler to some extend but we still use callbacks. So looking at the method registerUsers
, see it accepts an array of users and register them and once done it calls a complex callback. Just like we have used this callback at one place in the above implementation, we might use it at many places in our real word application, so to unit test all these type of scenario we might now want to skip the callback code to execute, we may want to do that if inside that call back we are calling a method from a different library or file but this is not the only usecase. Let’s now look at the second describe labelled as Stubs
, so the method registerUsers is called with an array of 3 objects and if we see the beforeEach block we can see we have made a stub object of callback and have simply passed it while making a call to registerUsers
and after that sinon does the trick see how we can easily check how many time callback got called, we can do much more not just this we can even check what arguments it was called with as to test it with more precision, so that’s all for now feel free to drop any comments or suggestions 🙂 , all the previous examples including the ones that are above are here, the following are my references
Sinon – https://sinonjs.org/releases/v7.3.2/
