Simon
Simon

Reputation: 1719

Stub module function called from the same module

I can't figure out a way to stub a function called from within the same module this function is defined (the stub does not seem to work). Here's an example:

myModule.js:

'use strict'

function foo () {
  return 'foo'
}

exports.foo = foo

function bar () {
  return foo()
}

exports.bar = bar

myModule.test.js:

'use strict'

const chai = require('chai')
const sinon = require('sinon')

chai.should()

const myModule = require('./myModule')

describe('myModule', () => {
  describe('bar', () => {
    it('should return foo', () => {
      myModule.bar().should.equal('foo') // succeeds
    })

    describe('when stubbed', () => {
      before(() => {
        sinon.stub(myModule, 'foo').returns('foo2') // this stub seems ignored
      })

      it('should return foo2', () => {
        myModule.bar().should.equal('foo2') // fails
      })
    })
  })
})

This reminds me of Java static functions which are not stubbable (almost).

Any idea how to achieve what I'm trying to do? I know that extracting foo in a different module will work, but that's not what I'm trying to do here. I'm also aware that invoking foo in the bar method with the keyword this will also work, I'm puzzled toward the use of ̀this in this context (since I'm not using OOP).

Upvotes: 14

Views: 3947

Answers (2)

Seb D.
Seb D.

Reputation: 5195

I was a bit wary of using exports since it's a bit magical (for instance when you're coding in Typescript, you never use it directly), so I'd like to propose an alternate solution, which still requires modifying the source code unfortunately, and which is simply to wrap the function to be stubbed into an object:

export const fooWrapper = {
    foo() {...}
}

function bar () {
    return fooWrapper.foo()
}

And sinon.stub(fooWrapper, 'foo'). It's a bit a shame having to wrap like that only for testing, but at least it's explicit and type safe in Typescript (contrary to to exports which is typed any).

Upvotes: 1

Dave Amit
Dave Amit

Reputation: 2309

I just tested this. And it works like charm.

'use strict'

function foo () {
  return 'foo';
}

exports.foo = foo;

function bar () {
  return exports.foo(); // <--- notice
}

exports.bar = bar;

Explanation

when you do sinon.stub(myModule, 'foo').returns('foo2') then sinon stubs the exported object's foo not the actually foo function from inside your myModule.js ... as you must know, foo is in accessible from outside the module. So when you set exports.foo, the exported object exports.foo stores the ref of foo. and when you call sinon.stub(myModule, 'foo').returns('foo2'), sinon will stub exports.foo and not the actual foo

Hope this makes sense!

Upvotes: 12

Related Questions