NodeJS: Testing require() Ridden Code

Nicholas Glenn
Helpful Human
Published in
3 min readFeb 17, 2015

We all know (hopefully) that we should be writing tests and decoupled, testable code. But often times, deadlines and expectations thwart our desire to write smart, efficient and technical debt free code. Unless your team already has some good standards and libraries in place, you’re likely going to have some refactoring to do before you can start writing proper tests. For many, this is a major blocker and may prevent tests getting written.

The most common cause for this issue in NodeJS is require(). A necessity for for any NodeJS application, require() can get us into some tightly-coupled dilemmas if we’re not careful. Let’s do some role-playing and and demonstrate this issue. Be forewarned: This is not an article on how to write tests in general!

// example.js
var db = require('./db')
module.exports = function (left, right) {
return db.insert({ sum: left + right })
})
// tests/example.js
var expect = require('chai').expect,
sum = require('../example')
describe('Sum', function () {
it('stores the sum correctly in the db', function () {
sum(5, 6).then(function (data) {
expect(data).to.be.an('object')
expect(data.sum).to.equal(11)
})
})
})

Here we have a module that requires access to our database connection so we can store a sum of 2 numbers in our database. We don’t want our tests to actually talk to our database as this could get messy really quickly. We might even want to test that we’re passing the correct information to our database’s insert() method. To do this, we need to pass a mocked instance in place of the database module… but how we can switch it out with our custom mock without refactoring our code?

What we’ll need to do is intercept Node’s require() function with one that will allow us to override a loaded module with a value of our choosing. There are a few different libraries that will allow us to accomplish this but the 2 more promising ones I found were require-hijack and proxyquire. For the sake of this article, let’s go with a the latter. Let’s rewrite the test from our example above using proxyquire.

// tests/example.js
var expect = require('chai').expect,
proxyquire = require('proxyquire'),
dbStub = {},
sum = proxyquire('../example', { './db': dbStub })
dbStub.insert = function (data) {
expect(data).to.be.an('object')
expect(data.sum).to.equal(11)
}
describe('Sum', function () {
it('stores the sum correctly in the db', function () {
sum(5, 6)
})
})

Ok, let’s dissect this real quick. First off, we brought in proxyquire, then we specified a mock (also known as a stub) for the db module. Instead of using require() to get our example module (sum), we’re using proxyquire and passing it our db stub whenever the module ./db is being requested. When using this library it’s important to point out that the module name we’re specifying to be replaced has to be the relative path from the file we’re testing, not the test itself. Below is an example to clarify this…

// example.js
var foo = require('./path/to/foo')
// test/example.js
var proxyquire = require('proxyquire'),
correct = proxyquire('../example', { './path/to/foo': {} }),
incorrect = proxyquire('../example', { '../path/to/foo: {} })

And that about wraps up the very basics of testing tightly-coupled NodeJS. However, though we’ve temporarily solved the problem and implemented testing, this isn’t the most maintainable solution. And though the proxyquire library has a lot of neat features to deal with new issues that this approach may introduce, I have one final statement I feel I need to make…

Hijacking require() should be a last resort.

Seriously, guys... If you can keep yourself out of the situation shown above by spending a little more time thinking through your code architecture, do it. Writing smaller, simpler and more maintainable code is always a good thing and rarely goes unrewarded.

--

--