How to spy on your required modules | Toucan Toco

Join
us

How to spy on your required modules

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

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