Search code examples
javascriptnode.jsunit-testingsinonstub

Sinon - Stub module function and test it without dependency injection


I have a proxy module, which forwards function calls to services. I want to test if the service function is called, when a function in this proxy module is called.

Here is the proxy module:

const payService = require('../services/pay')
const walletService = require('../services/wallet')

const entity = {
    chargeCard: payService.payByCardToken,
    // ... some other fn
}

module.exports = entity

Based on this example and this response, I tried to stub the required module 'payService':

const expect = require('expect.js')
const sinon = require('sinon') 
const entity = require('../entity')
const payService = require('../../services/pay')

describe('Payment entity,', () => {

    it('should proxy functions to each service', () => {

        const stub = sinon.stub(payService, 'payByCardToken')
        entity.chargeCard()
        expect(payService.payByCardToken.called).to.be.ok()

    })
})

But the test fails with:

  0 passing (19ms)
  1 failing

  1) Payment entity,
       should proxy functions to each service:
     Error: expected false to be truthy
      at Assertion.assert (node_modules/expect.js/index.js:96:13)
      at Assertion.ok (node_modules/expect.js/index.js:115:10)
      at Function.ok (node_modules/expect.js/index.js:499:17)
      at Context.it (payments/test/entity.js:14:56)

And that's because payService module isn't really stubbed. I know if I add payService as a property of entity and wrap everything with a function, the test will pass:

// entity
const entity = () => {
    return {
        payService,
        chargeCard: payService.payByCardToken,
        // .. some other fn
    }
}

// test
const stub = sinon.stub(payService, 'payByCardToken')
entity().chargeCard()
expect(payService.payByCardToken.called).to.be.ok()

// test output
Payment entity,
  ✓ should proxy functions to each service

1 passing (8ms)

But that's code added only for testing puposes. Is there a way to a way to stub module functions without dependency injection and workarounds?


Solution

  • The problem is that you're stubbing payService too late, after entity has already set its mappings.

    If you change your test code like so:

    const expect = require('expect.js')
    const sinon = require('sinon') 
    const payService = require('../../services/pay')
    
    describe('Payment entity,', () => {
        let entity
    
        before(() => {
            sinon.stub(payService, 'payByCardToken')
            entity = require('../entity')
        })
    
        it('should proxy functions to each service', () => {
            entity.chargeCard()
            expect(payService.payByCardToken.called).to.be.ok()
        })
    })
    

    ...you should find that entity sets itself up with your stubbed function and the assertion passes okay.