Search code examples
node.jsunit-testingtestingtddsinon

Mocking constructor functions in node


How do other node developers who use sinon mock out constructor calls within their unit tests? For example, suppose I have some function foo

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

and in a separate test file I have some test in which I want to verify what the Dependency constructor is called with (args), and I need to control what it returns

it('should call Dependency constructor with bar', function() {
  var foo = require('myModule').foo
  var DependencyMock; //code to make the mock

  foo();
  expect(DependencyMock.calledWith(bar)).to.equal(true);
});

The problem is that sinon can only mock functions attached to an object, so we have to attach the constructor to an object before it can be mocked.

What I've been doing is just making an object to attach the constructor to in the module making the constructor call, calling the constructor as a method of that object, then exporting the object to use it in tests:

var Dependency = require('path/to/dependency');

var namespace = {
  Dependency: Dependency
}

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

testfile:

it('should call Dependency constructor with bar', function() {
  var foo = require('myModule').foo;
  var namespace = require('myModule').namespace;

  var DependencyMock = sinon.mock(namespace, 'Dependency').returns(0);
  foo();
  expect(DependencyMock.calledWith(bar)).to.equal(true);
});

This works, but it feels really clunky to expose an object on my module just for the sake of testing it.

Any tips?


Solution

  • 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