«

Chaining Sinon Mocks

Update

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();
};
Share Comment on Twitter