Search code examples
node.jssinonsinon-chai

Sinon stub replacing function not working


I am isolated the problem I am facing in my nodeJs here. Sinon stubbing on a dependent function is not working as expected. I didn't get what I am missing here. Appreciate help. Here is the sample code.

sinonTest.js

"use strict";
function getSecretNumber () {
  return 44;
}
function getTheSecret () {
  return `The secret was: ${getSecretNumber()}`;
}

module.exports = {
  getSecretNumber,
  getTheSecret,
};

sinonTest_spec.ts

"use strict";
const sinon = require("sinon");
const sinonMediator = require("./sinonTest");
const assert = require("assert");

describe("moduleUnderTest", function () {
  describe("when the secret is 3", function () {
    beforeEach(function () {
      sinon.stub(sinonMediator, "getSecretNumber").returns(3);
    });
    afterEach(function (done) {
      sinon.restore();
      done();
    });
    it("should be returned with a string prefix", function () {
      const result = sinonMediator.getTheSecret();
      const stubValue = sinonMediator.getSecretNumber();    
      assert.equal(stubValue, 3);                         //this assertion passed
      assert.equal(result, "The secret was: 3");          //but this assertion failed.
    });
  });
});

Here is the assertion error I am getting when I executed test case.

AssertionError [ERR_ASSERTION]: 'The secret was: 44' == 'The secret was: 3'

Thank you.


Solution

  • This is the commonjs behavior that when you require a module. More details: https://nodejs.org/docs/latest/api/modules.html#modules_exports_shortcut

    function require(/* ... */) {
      const module = { exports: {} };
    
      ((module, exports) => {
        function getSecretNumber() {
          return 44;
        }
        function getTheSecret() {
          return `The secret was: ${getSecretNumber()}`;
        }
        module.exports = {
          getTheSecret,
          getSecretNumber,
        };
      })(module, module.exports);
    
      return module.exports;
    }
    

    You can stub module.exports.getSecretNumber method, but the getSecretNumber function called within getTheSecret is still the original function, not the stub one. That's why your stub not working.

    index.js:

    'use strict';
    function getSecretNumber() {
      return 44;
    }
    function getTheSecret() {
      return `The secret was: ${exports.getSecretNumber()}`;
    }
    
    exports.getSecretNumber = getSecretNumber;
    exports.getTheSecret = getTheSecret;
    

    index.test.js:

    'use strict';
    const sinon = require('sinon');
    const sinonMediator = require('./');
    const assert = require('assert');
    
    describe('moduleUnderTest', function() {
      describe('when the secret is 3', function() {
        beforeEach(function() {
          sinon.stub(sinonMediator, 'getSecretNumber').returns(3);
        });
        afterEach(function(done) {
          sinon.restore();
          done();
        });
        it('should be returned with a string prefix', function() {
          const result = sinonMediator.getTheSecret();
          const stubValue = sinonMediator.getSecretNumber();
          assert.equal(stubValue, 3); 
          assert.equal(result, 'The secret was: 3');
        });
      });
    });
    

    unit test results with coverage report:

      moduleUnderTest
        when the secret is 3
          ✓ should be returned with a string prefix
    
    
      1 passing (25ms)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |      75 |      100 |      50 |      75 |                   
     index.js |      75 |      100 |      50 |      75 | 3                 
    ----------|---------|----------|---------|---------|-------------------