Mocking constructor functions in node

I think it's worth asking why you'd want to mock a constructor of a dependency instead of injecting that dependency?

Consider your example code:

// in "foo.js"
function foo() {
  var dependency = new Dependency(args);
  // do stuff with dependency
}
exports.module.foo = foo;

If Dependency is required for foo to work you can inject it as an argument of foo:

// in "foo.js"
function foo(dependency) {
  // do stuff with dependency
}
exports.module.foo = foo;

// in "bar.js"
var foo = require('./foo.js')(new Dependency(args));

With this change it's now trivial to inject any Test Double in your tests (to find out more about JavaScript Test Doubles have a look at my article on the subject).

This approach makes the dependencies of your function/module explicit, but requires you to wire them up at some point (here: require('./foo.js')(new Dependency(args));).


If you didn't want to wire things up manually there's another approach you can take using rewire and replacing constructor with factory method:

// in "dependency.js"
module.exports= function(args) {
  return new Dependency(args);
}

// in "foo.js"
var dependency = require('./dependency');

function foo() {
  var dep = dependency(args);
  // do stuff with dependency
}
exports.module.foo = foo;

and in your test:

var rewire = require("rewire"),
    foo    = rewire("../lib/foo.js");

it('should call dependency... ', function() {
   foo.__set__("dependency", /* some spy */ );

   foo();
});

Hope this helps!

Jan


I used a workaround for this:

// Keep a reference to your dependancy class.
const dependencyClass = Dependency;
let item = new Dependency();
// Replace class with a stub.(optionally return an item.
Dependency = sinon.stub(Dependency, 'constructor').returns(item);

// Call your code that you expect the class constructor to be called.
foo();

assert.isTrue(Dependency.calledWithNew());
assert.equal(1, Dependency.callCount);
// Restore class reference.
Dependency = dependencyClass;

Additionally, in the above case, an item is returned, so the user can have access to the dependency for further testing. eg.

assert.equal(item.someValue, 10);

You can have other problems using this, eg defined properties will no longer be available for the class. I agree with Jan Molek, use this in case you cannot change the code.