Dependency Injection and CommonJS in Node.js Unit Testing

Daiwei
2 min readJan 13, 2016

--

Dependency injection (DI) is an often used technique to manage dependencies of a module. It’s great for testing, because we can control the dependencies of a module from outside. This concept is widely used in Angular 1.x. In Node.js we have similar dependency management API called CommonJS.

// index.js
var readFile = require('./readFile');
writeFile();

The difference is “index.js” can find dependent module itself. Outsiders have no control over what is added. This is simple for dependency management. But we lost the benefit of DI in testing, because we no longer have control outside of a module.

Many approaches can be taken to remedy that. We can export everything on an object.

var readFile = require('./readFile').readFile;

This way we can require “readFile” module before test case and replace “readFile” method with a stub. This is where libraries like sinon.js comes into play. But we have to remember to restore the stubbed function otherwise we won’t get accurate results of other tests. A test runner will often help us with a “afterEach” block.

Other problem exists though. How to stub 3rd party modules that don’t export on a namespace? How to bust cache of required module while testing different cases? CommonJS did all those for the convenience of production code, but those features only make tests harder to write. Are we going to create our own DI logic just because we want to test?

Fortunately, Proxyquire can help us.

Stubbing modules without namespace

// module.js
var func = require(‘func’);
module.export = function say() {
func(); // => ‘hello world!’;
};
// test.js
var proxyquire = require(‘proxyquire’);
it(‘should invoke func’, function () {
var subject = proxyquire(‘./module’, {
func: function () {
console.log(‘I am testing’);
}
});
subject(); // Will log ‘I am testing’ instead of ‘hello world!’
});

No need to write “afterEach” to clean up

Every time proxyquire requires a module, it will require a fresh copy of it. It won’t cache like CommonJS. So what’s being stubbed is not stubbed when required again.

State is trouble, we avoid managing states in production code, why not test?

Not write “afterEach” is a powerful thing. It made testing more functional, less stateful. You don’t have to keep track of things your changed before or during a test. You can throw away testing artifacts when you finish. You will always start fresh.

Proxyquire pattern is dependency injection pattern

No need to say more. Proxyquire unobtrusively introduces DI without us having to bend the rule of CommonJS.

Stub the universe

Because we can inject things we want, now we can stub everything and only allow defined behaviors. Unexpected behaviors can be detected earlier.

Conclusion

Dependency injection is a powerful pattern. But something it could be overwhelming in production code. CommonJS is a good enough API for us. Using Proxyquire will help us to enjoy the benefits of DI without hurting the elegance of our production code.

--

--