Tuesday, 25 April 2017

Stubbing constructors in JS

If a  constructor is exported directly from a module, it cannot be stubbed in any module that requires it directly.

For instance

========= MyConstructor.js =========
module.exports = function(){ 
  console.log('MyConstructor called'); 
};

and a SUT

========= MyConsumer.js =========
var MyConstructor = require('./MyConstructor');
function MyConsumer(){
    this.useful = new MyConstructor();
};
module.exports = MyConsumer;

You cannot stub the constructor. From my understanding the ECMAScript spec doesnt provide hooks for the new() operator. 

Lets look at some workarounds.

1) Use an init method. This involves using a constructor which calls an init() method. The init() method can then be stubbed out in the usual way. 

2) Stub the constructor using rewire

// 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();
});

3) Dependency injection

http://stackoverflow.com/questions/33702588/mocking-constructor-functions-in-node

// 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));

No comments:

Post a Comment