This is a quick article to share a little tip on spies and requires
Our test stack uses mocha, karma, sinon. Our code is bundled using webpack
Front-end testin is hard: there are lots of libraries but only a few practical examples. When you start testing your project, you quickly run into problems and you think : “I’m pretty sure someone has been in this position before, why can’t I find any documentation ?”.
Since I was getting frustrated I decided to make my own documentation.
I recently spent some time to understand how I could spy on a module required by the module we were testing… Get it?
If not, just read on ;)
A simple case
Let’s say you have a main js module, that requires some other module:
var yeah = require('yeah');
var great = function () {
// Do great stuff
// Use the required module
yeah();
}
module.exports = great
Let’s use a spy
What we want to do
- Check if
yeah
was called - Test code only: no change to the
great
module (it’s great already)
You would like to write something similar to this
// In your test file
var sinon = require('sinon');
var great = require('great');
describe('Great', function() {
var yeahSpy;
beforeEach(function(){
yeahSpy = sinon.spy(great, 'yeah');
});
it('should call yeah', function(){
great();
yeahSpy.should.have.been.called;
});
afterEach(function(){
yeahSpy.restore();
});
});
But this doesn’t work.
It will throw a TypeError: Attempted to wrap undefined property yeah as function
.
Solution with code modification
Note that I don’t recommend this solution, but I am putting it here for the sake of completeness.
You could modify your great
module so that yeah
is a property.
var yeah = require('yeah');
var great = function () {
// Do great stuff
// Here you need to use the yeah which is a property of great for this to work
great.yeah();
};
great.yeah = yeah;
module.exports = great;
I don’t like this method mainly because these changes don’t really make sense: if not for testing, I would not have done it.
Luckily, there is a better way !
Using rewire
Rewire let’s you require modules and adds getters and setters that help you in your tests.
The trick here is to get the yeah
module with rewire, create a spy on it, and inject the spy back into great
.
var sinon = require('sinon');
// Here use rewire instead of require
var great = rewire('great');
// Here get the `yeah` module
var yeah = great.__get__('yeah');
describe('Great', function() {
var yeahSpy;
beforeEach(function(){
// Create a spy on the correct yeah module
yeahSpy = sinon.spy(yeah);
// Inject the spy in the module, otherwise it doesn't work
great.__set__('yeah', yeahSpy);
});
it('should call yeah', function(){
great();
yeahSpy.should.have.been.called;
});
afterEach(function(){
yeahSpy.restore();
});
});
And you’re good to go !
Note: you might be able to achieve similar results with Proxyquire. We tried very briefly but rewire seemed simpler so we went with it.
With webpack
For rewire to work with webpack, use … rewire-webpack
. The configuration is easy (shamelessly copy-pasted from rewire webpack’s doc)
// In your webpack config file
var RewirePlugin = require("rewire-webpack");
var webpackConfig = {
plugins: [
new RewirePlugin()
]
};
If you’re interested in other front-end testing stuff, check out David’s article on visual TDD