Testing JavaScript Modules

October 13, 2015
In our previous post we outlined benefits of writing JavaScript in modules. One benefit we didn’t mention was how much more testable JavaScript modules are. In your test file you can require the module under test and let the require system import any dependencies. The goal of this post is to outline some of the common testing patterns of JavaScript modules and highlight one of the libraries we have open-sourced to help make testing private state in JavaScript modules even easier.

Public Functions

Below is an example of a utilities file that simply exposes an object with some functions. This is the easiest example of a module to test because all functions are public.
'use strict';
 
var Utils = {
  add: function(num1, num2) {
    return num1 + num2;
  }
};
 
module.exports = Utils;
Code language: JavaScript (javascript)
We simply require the file and write tests that the add function works as expected.
'use strict';
 
var utils = require('./utils');
 
describe('utils', function() {
  describe('#add', function() {
    it('should add two numbers', function() {
      var actual = utils.add(2, 4);
      assert.equal(actual, 6);
    });
  });
});
Code language: JavaScript (javascript)

Private Functions

Simple Objects

Building on our example from above, here is the same module, but with a private helper function.
'use strict';
 
function add(num1, num2) {
  return num1 + num2;
}
 
var Utils = {
  add2: function(num) {
    return add(num, 2);
  }
};
 
module.exports = Utils;
Code language: JavaScript (javascript)
When we require the utils module, we can’t access Utils.add. We want to make it visible in the test environment in order to test that function, but not visible to the rest of our code base. Here at Wealthfront we have built privatestate, a testing library to expose private state of JavaScript modules in our test environment. Below are some examples that outline its benefits. Check out its documentation for more detail! privatestate lets us expose the private functions like this:
var privateState = require('privatestate');

// ... code
privateState.registerTestingFunctions(Utils, ['add']);
 
module.exports = Utils;
Code language: JavaScript (javascript)
Allowing us to write tests against add.
var privateState = require('privatestate');
var utils = require('./utils');

describe('utils#add', function() {
  it('should add two numbers', function() {
    var add = privateState.getForTesting(utils, 'add');
    var actual = add(2, 4);
    assert.equal(actual, 6);
  });
});
Code language: JavaScript (javascript)

Functions

Similarly to exposing a function on objects, we can expose helper functions on functions the same way.
'use strict';
 
var privateState = require('privatestate');
 
function nameHelper(first, last) {
  return first + ' ' + last;
}
 
function Person(first, last) {
  this.first = first;
  this.last = last;
}
 
Person.prototype = {
    welcome: function() {
        return 'My name is ' + nameHelper(this.first, this.last);
    }
}
 
privateState.registerTestingFunctions(Person, ['nameHelper']);
 
module.exports = Person;
Code language: JavaScript (javascript)

Stubbing

Sometimes we want to replace our private state and dependencies when we are in test to help make testing higher level functionality easier. We will use sinon in these examples.

Private Functions

We’ve seen how to properly expose a private function when in a testing environment, but what if we want to stub that function and replace it with something else? Expose it as we did previously:
var privateState = require('privatestate');
 
function formatString(date) {
  return date.toDateString();
}
 
var Utils = {
  getDateString: function(date) {
    return 'today is ' + formatString(date);
  }
};
 
privateState.registerTestingFunctions(Utils, ['formatString']);
 
module.exports = Utils;
Code language: JavaScript (javascript)
And stub it out in our test:
var privateState = require('private-state');
var sinon = require('sinon');
var utils = require('./utils');
 
describe('utils#getDateString', function() {
  it('should print value from formatString', function() {
    var stub = sinon.stub().returns('today');
    privateState.setFunctionForTesting(utils, 'formatString', stub);
 
    var now = new Date(2015, 1, 1);
    var dateString = utils.getDateString(now);
 
    assert.equal(dateString, 'today is today');
  });
});
Code language: JavaScript (javascript)

Dependencies

Stubbing dependencies is extremely important for testing JavaScript modules as modules often have multiple dependencies. We use Proxyquire (and its family of related packages). Proxyquire lets you pass your require call an object of stubs to use instead of module dependencies.
var Helper = {
  msg: 'from helper'
};
 
module.exports = Helper;
Code language: JavaScript (javascript)
var helper1 = require('./helper');
 
var Utils = {
  getString: function() {
    return helper1.msg;
  }
};
 
module.exports = Utils;
Code language: JavaScript (javascript)
If we are testing utils.js and we want to verify that getString returns helper1.msg, we want to stub out Helper so that we can specify msg. Using Proxyquire, we pass the require statement for Utils our stub for Helper.
var proxyquire = require('proxyquire');

describe('utils#getString', function() {
  it('should print helper.msg', function() {
    var HelperStub = {
      msg: 'stub msg'
    };
 
    var utils = proxyquire('../server/utils', {
      './helper': HelperStub
    });
 
    assert.equal(utils.getString(), 'stub msg');
  });
});
Code language: JavaScript (javascript)

Wrapping Up

JavaScript modules help improve the development and test lifecycles by being able to work on standalone modules and expose only the functionality we want depending on the execution environment. While making things only accessible in the module itself is beneficial, it can also make things harder to test. Thankfully privatestate and Proxyquire enable us to test all of those hard-to-reach places. Go forth and test all of the things!