How to implement partial subbing in JS with sinon?

269 views Asked by At

Sometimes, in a module, functions call other functions (for factorization, code abstraction, etc...), and we may want to test the caller function without testing the inside function.

However, this code does not work as is:

// src/my-module.js

function externalFunction() {
  return internalFunction();
}

function internalFunction() {
  return { omg: 'this is real' };
}

module.exports = {
  externalFunction,
};
// test/my-module.spec.js

const { assert } = require('chai');
const {
  describe,
  it,
  before,
  beforeEach,
} = require('mocha');
const sinon = require('sinon');

let myModule;

describe('externalFunction', () => {
  before(() => {
    myModule = require('../src/my-module');
  });

  beforeEach(() => {
    // partial stubbing not working
    sinon.stub(myModule, 'internalFunction').callsFake(() => ({ omg: 'this is fake' }));
  })

  it('returns the result of internalFunction', () => { // FAILING
    const result = myModule.externalFunction();
    assert.deepEqual(result, { omg: 'this is fake' });
  });
});
1

There are 1 answers

0
Pleymor On

The reason why it does not work is actually a simple problem of references on the function.

As we used return internalFunction(), the referenced function targets the real one, while sinon.stub(myModule, 'internalFunction') actually creates a reference in the exported functions.

Then to have it work, we need to export internalFunction and explicitly use module.exports.internalFunction() in externalFunction

// src/my-module.js

function externalFunction() {
  // note the use of module.exports here
  return module.exports.internalFunction();
}

function internalFunction() {
  return { omg: 'this is real' };
}

module.exports = {
  externalFunction,
  // note we export the internal function here
  internalFunction,
};
// test/my-module.spec.js

const { assert } = require('chai');
const {
  describe,
  it,
  before,
  beforeEach,
} = require('mocha');
const sinon = require('sinon');

let myModule;

describe('externalFunction', () => {
  before(() => {
    myModule = require('../src/my-module');
  });

  beforeEach(() => {
    // partial stubbing: it actually stubs the exported function
    sinon.stub(myModule, 'internalFunction').callsFake(() => ({ omg: 'this is fake' }));
  })

  it('returns the result of internalFunction', () => {
    const result = myModule.externalFunction();
    assert.deepEqual(result, { omg: 'this is fake' });
  });
});