Mocking calls to a class constructor in Javascript

1 minute read

The other day, whilst writing tests with SinonJS I realised that there was no obvious way of mocking calls to class constructors in Javascript. A quick search for “mocking Javascript class constructor” did lead me to some helpful answers.

Generally speaking, you can only mock a method which exists against an object. So in order to mock the MyClass constructor you have to mock the MyClass method on its container object:

var sinon = require('sinon');

exports.MyClass = function() {
  this.a = 1;
};

var spy = sinon.spy(exports, 'MyClass');

var inst = new exports.MyClass();

console.log(spy.calledOnce); // true

The above example is for Node. In the browser the global object to which top-level functions automatically belong is window. Note though what happens if you add a local variable reference into the mix:

var sinon = require('sinon');

var MyClass = exports.MyClass = function() {
  this.a = 1;
};

var spy = sinon.spy(exports, 'MyClass');

var inst = new MyClass();

console.log(spy.calledOnce); // false

This discrepancy occurs because Sinon wraps exports.MyClass with its own mechanism, which means that the MyClass local variable which points directly to the constructor remains unaffected. To prove the point:

var sinon = require('sinon');

exports.MyClass = function() {
  this.a = 1;
};

var spy = sinon.spy(exports, 'MyClass');

var MyClass = exports.MyClass;

var inst = new MyClass();

console.log(spy.calledOnce); // true

Calling from a sub-class

If you are calling a base class constructor from within a subclass you will generally be writing something like this:

var util = require('util'); // core node.js module

var Controller = function() {};

var DefaultController = function() {
  Controller.apply(this, Array.prototype.slice.call(arguments, 0));
};
util.inherits(DefaultController, Controller);

In such instances you can mock as previously noted. Alternatively you can mock the call to apply():

var sinon = require('sinon');

var util = require('util'); // core node.js module

var Controller = function() {};

var DefaultController = function() {
  Controller.apply(this, Array.prototype.slice.call(arguments, 0));
};
util.inherits(DefaultController, Controller);

var spy = sinon.spy(Controller, 'apply');

var inst = new DefaultController();

console.log(spy.calledOnce); // true

Leave a Comment