Search code examples
javascriptnode.jssinonchai

How to sinon spy module export utility functions


In javascript (ES6), I have a utility module which just contains some functions, and then in the end of the file, I export them like so:

module.exports = {
  someFunction1,
  someFunction2,
  someFunction3,
}

Then I want to write unit tests for those functions. Some of the functions depend on each other; they call each other in a way, that for example, someFunction1 might call someFunction2. There's no circular problems.

Everything works well, until I need to spy that one of the functions is called. How can I do it? Currently I'm using Chai and Sinon.

In the test file, I have imported the whole file as a module:

const wholeModule = require('path/to/the/js/file')

And finally, my test looks like following:

it('should call the someFunction2', (done) => {
  const spy = sinon.spy(wholeModule, 'someFunction2')

  wholeModule.someFunction1() // someFunction2 is called inside someFunction1

  assert(spy.calledOnce, 'someFunction2 should be called once')
  done()
})

The problem is, that the test fails, because in someFunction1, the someFunction2 function is used directly. I apply the spy to the module object's function. But that's a different object. Here's an example of the someFunction1:

function someFunction1() {
  someFunction2()
  return 2;
}

I know the reason why it won't work, but I don't know what would be the best practise in this case to make it work? Please help!


Solution

  • You can use rewire module. Here is an example:

    Source code:

    function someFunction1() {
      console.log('someFunction1 called')
      someFunction2();
    }
    
    function someFunction2() {
      console.log('someFunction2 called')
    }
    
    module.exports = {
      someFunction1: someFunction1,
      someFunction2: someFunction2
    }
    

    Test case:

    'use strict';
    
    var expect = require('chai').expect;
    var rewire = require('rewire');
    var sinon = require('sinon');
    
    var funcs = rewire('../lib/someFunctions');
    
    it('should call the someFunction2', () => {
      var someFunction2Stub = sinon.stub();
      funcs.__set__({
        someFunction2: someFunction2Stub,
      });
    
      someFunction2Stub.returns(null);
    
      funcs.someFunction1();
    
      expect(someFunction2Stub.calledOnce).to.equal(true);
    });