node.jsunit-testingmocha.jssinon

Make an internal function a stub


Lots of similar questions, but none that seem to fit.

Say there's a nodejs function:

async function handler(event) {
   await parseEvent(event)

   if(event.a)
      await doBusinessLogicA(event)
   else
      await doBusinessLogicB(event)

   return response
}

async function doBusinessLogicA(event){ ... }
async function doBusinessLogicB(event){ ... }

In theory, a unit test for "handler" shouldn't be testing businessLogic A or B. But how do I make those two calls do nothing?

The tests are written using mocha and sinon:

const sinon = require('sinon')

const { handler } = require('./location-whatever')

describe("Lambda tests", () => {
   describe("Handler", async () => {
      it("Should enter first if statement", async () => {
         let results = await handler({a: true})
      }
   }
}

I've tried using a sinon sandbox, and have also found a way manipulating the module.exports to become stubs but can't find a good way to restore them.

I know that this function doesn't do much, but for the sake of knowing the proper techniques I'm struggling to figure out how to do it "right."


Solution

  • You can use rewire package.

    rewire adds a special setter and getter to modules so you can modify their behaviour for better unit testing.

    Which means you can get/set the private functions defined in module scope.

    e.g.

    handler.js:

    async function handler(event) {
        if (event.a) await doBusinessLogicA(event);
        else await doBusinessLogicB(event);
    
        return 'response';
    }
    
    async function doBusinessLogicA(event) {
        console.log('real implementation');
    }
    async function doBusinessLogicB(event) {
        console.log('real implementation');
    }
    
    module.exports = { handler };
    

    handler.test.js:

    const rewire = require('rewire');
    const mod = rewire('./handler');
    const { expect } = require('chai');
    
    describe('78694661', () => {
        it('should pass when a is true', (done) => {
            mod
                .__with__({
                    doBusinessLogicA: async () => console.log('doBusinessLogicA fake implementation'),
                })(() => mod.handler({ a: true }))
                .then((r) => {
                    expect(r).to.be.equal('response');
                    done();
                });
        });
    
        it('should pass when a is false', (done) => {
            mod
                .__with__({
                    doBusinessLogicB: async () => console.log('doBusinessLogicB fake implementation'),
                })(() => mod.handler({ a: false }))
                .then((r) => {
                    expect(r).to.be.equal('response');
                    done();
                });
        });
    });
    

    Test result:

      78694661
    doBusinessLogicA fake implementation
        √ should pass when a is true
    doBusinessLogicB fake implementation
        √ should pass when a is false
    
    
      2 passing (8ms)
    

    "rewire": "^4.0.1"