read

Mocking, stubbing and strategies for dependency injection are often overly complex parts of the JavaScript test code we have to write. But they help us isolate the unit that we want to test. Since CommonJS modules often act as a natural seam for a unit, it makes perfect sense that test frameworks like Jest automatically mock CommonJS dependencies.

While Jest looks absolutely awesome and can do other great things like like parallel testing, runAllTimers and provide promise helpers, Karma and Jasmine are still my weapons of choice and firmly fastened to the old utility belt… Especially now that I’ve found the karma nyan reporter ;)

But I’m not here to talk about karma reporters, as colourful as they can be…

So how do we stub CommonJS Dependencies?

proxyquire is a super easy to use proxy for requiring modules, that takes an object literal of stubbed dependencies as it’s second argument.

In the following example we’re going to test a member service, which has a create method and for the purposes of testing we want to isolate it from the ajax service call.

member.js

1
2
3
4
5
6
7
8
9
10
11
12
var ajax = require('./ajax');
var member = {
create: function(firstName, surname) {
return ajax.post({
firstName: firstName,
surname: surname
});
}
};
module.exports = member;

In our test spec, we’ll require member.js via proxy and stub out the ajax service to return a successfully resolved promise containing a member number.

member.spec.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var proxyquire = require('proxyquire');
// use a helper to return a promise like the ajax post
// method would
var promiseStub = require('promiseStub');
var ajaxStub = {
post: function() {
return promiseStub.success('MEM1234');
}
};
var member = proxyquire('../member', {
'./ajax' : ajaxStub
});
describe('using proxyquire', function() {
var memberNumber;
beforeEach(function (done) {
member.create('Peter', 'Rabbit').then(function (data) {
memberNumber = data;
done();
});
});
it('subsitutes the required module for the stub', function() {
expect(memberNumber).toBe('MEM1234');
});
});

It’s worth noting that the path to the stubbed module (ie. './ajax') is relative to the location of member.js, not member.spec.js.

Browserify + proxyquire = proxyquireify

@tlorenz, the smart guy behind proxyquire also put together proxyquireify for use with browserify, and the usage is near identical to the above CommonJS examples except for a couple of reference changes.

Heres a walkthrough of steps to get it working with Browserify and Karma :

  • Install the proxyquireify node module

    npm install proxyquireify --save-dev

  • Edit your karma.conf.js file

    • put var proxyquire = require('proxyquireify'); at the top of the file
    • in the browserify section, add a configure function to add the proxyquire plugin to browserify and set the root folder for the tests.

      1
      2
      3
      4
      5
      configure: function(bundle) {
      bundle
      .plugin(proxyquire.plugin)
      .require(require.resolve('./src/javascript/__tests__'), { entry: true });
      }

      you can find my full karma.conf.js in a gist here

  • Create some test specs

    • create an index.js file in the folder you used in the configure section in karma and reference all the specs you want to run by requiring them with standard require calls.

      so given a structure like this…

      1
      2
      3
      4
      5
      /__test__
      index.js
      /spec
      auth.spec.js
      member.spec.js

      index.js would look like this :

      1
      2
      require('./spec/auth.spec.js');
      require('./spec/member.spec.js');

      And in the spec files themselves we just pull in proxyquire with var proxyquire = require('proxyquireify')(require);

      eg.

      member.spec.js

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      var proxyquire = require('proxyquireify')(require);
      // use a helper to return a promise like the ajax post
      // method would
      var promiseStub = require('promiseStub');
      var ajaxStub = {
      post: function() {
      return promiseStub.success('MEM1234');
      }
      };
      var member = proxyquire('../member', {
      './ajax' : ajaxStub
      });
      describe('using proxyquire', function() {
      var memberNumber;
      beforeEach(function (done) {
      member.create('Peter', 'Rabbit').then(function (data) {
      memberNumber = data;
      done();
      });
      });
      it('subsitutes the required module for the stub', function() {
      expect(memberNumber).toBe('MEM1234');
      });
      });

And thats it, Happy stubbing!

Blog Logo

Stephen James

JavaScript and Front-End Developer


Published

Proudly published with Hexo
Image

Stephen James' Code Blog

Back to Overview