AFMeirelles
AFMeirelles

Reputation: 419

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?

Upvotes: 7

Views: 16225

Answers (1)

Phil Booth
Phil Booth

Reputation: 4907

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.

Upvotes: 10

Related Questions