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
|
|
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
|
|
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 aconfigure
function to add the proxyquire plugin to browserify and set the root folder for the tests.12345configure: 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
- put
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 standardrequire
calls.so given a structure like this…
12345/__test__index.js/specauth.spec.jsmember.spec.js…
index.js
would look like this :12require('./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
123456789101112131415161718192021222324252627282930var proxyquire = require('proxyquireify')(require);// use a helper to return a promise like the ajax post// method wouldvar 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!