joelgriffith.net
  • About
  • GitHub
  • Twitter
  • LinkedIn
  • Resume

Chaining Sinon Mocks

December 01, 2014

Sinon actually already has a nice API for doing this out of the box without any prototype mucking:

/**
 * test.mpsec.js
 */
var sinon = require('sinon');
var expect = require('chai').expect;
var rewire = require('rewire');
var main = rewire('main');
var CoolModuleMock = {
  start: sinon.stub().returnsThis(),
  end: sinon.stub().returnsThis(),
  pause: sinon.stub().returnsThis()
};

I’ll leave my post below for historical purposes, but please just use the code above :)


I’ve recently been writing a lot of unit-tests for some open-source projects, and one of those projects chained method calls to the node rsync module. I mocked out this dependency, but had to make sinon ‘chainable’ in order to properly test the method calls. It was an interesting thing to implement, so I thought I’d share this bit of code:

/**
 * test.mpsec.js
 */
var sinon = require('sinon');
var expect = require('chai').expect;
var rewire = require('rewire');
var main = rewire('main');
var CoolModuleMock = (function() {
    function CoolModuleMock() {}

    CoolModuleMock.prototype.start = sinon.stub().returns(CoolModuleMock.prototype);
    CoolModuleMock.prototype.pause = sinon.stub().returns(CoolModuleMock.prototype);
    CoolModuleMock.prototype.end = sinon.stub().returns(CoolModuleMock.prototype);

    return CoolModuleMock;
})();

// Overwrite the `CoolModule` reference to refer to our mock
main.set__('CoolModule', CoolModuleMock);

describe('Cool Module', function() {
  it('should call the `start`, `pause`, and `end` methods', function() {
    main();
    expect(CoolModuleMock.start.called).to.be.true();
    expect(CoolModuleMock.pause.called).to.be.true();
    expect(CoolModuleMock.end.called).to.be.true();
  });
});

/**
 * main.js
 */
var CoolModule = require('cool-module'); // rewire will overwrite this
var cool = new CoolModule();

module.exports = function() {
  cool.start();
  cool.pause();
  cool.end();
};