7

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?

AFMeirelles
  • 409
  • 3
  • 8
  • 25

1 Answers1

9

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.

Phil Booth
  • 4,853
  • 1
  • 33
  • 35
  • If payService is a module (with a bunch of routines, not a class, whose test instance fades away with the end of the test…) remember to [.restore()](http://sinonjs.org/releases/v1.17.7/stubs/#properties) to remove the stub to avoid side effects for next test… – Frank N Jun 27 '18 at 10:53
  • where can I read more about this topic? why the initial code does not work? is doing requires at runtime a good idea? – user1398619 Feb 01 '20 at 16:06