Search code examples
javascriptclassmodulemockingsinon

When unit testing a class method, how do I mock a function it calls--one defined in an imported module?


I am unit testing a particular method, and am having issues mocking another function that is called during the process. In my case, the method to test is defined in a class, and the function I'd like to mock is defined in a separate module. How do I mock this function? See below for my code.

In the past, I've used the Sinon package to mock/stub a dependency (example). But that doesn't work in this case. This is the first time I'm testing a method defined in a class, so perhaps that's why mocking the dependency isn't working.


My Code

Module Containing Test Function (myLib/myDir/combo.js)

const { externalFunction } = require('./external-function')
class Combo {
  constructor(props) {}
  async myMethod () {// The function under test.
    externalFunction()
  }
}
const myCombo = props => new Combo(props)
module.exports = { myCombo }

My Test File (test/myLib/myDir/combo.test.js); no attempt at mocking

const { myCombo } = require('../../../myLib/myDir/combo')

const comboObj = myCombo({}) // Instantiate object to expose method to test.
await comboObj.myMethod()// Call method that I want to test.  This throws type error because myMethod function calls externalFunction, which throws an error in the test environment.

My Test File (test/myLib/myDir/combo.test.js); attempt to use Sinon package to mock

const sinon = require('sinon')

const dependencyModule = require('./external-function')// Defines the method dependencyModule.methodToMock

const myStub = sinon.stub(dependencyModule, 'methodToMock').returns(555) // Stubs dependencyModule.methodToMock and ensures it always returns the value: 555.

const comboObj = myCombo({}) // Instantiate object to expose method to test.

await comboObj.myMethod()// Call method that I want to test.  This throws type error because myMethod function calls externalFunction, which throws an error in the test environment.

Solution

  • How? You need to follow "stubbed module can not be destructured." on the official guide How to stub a dependency of a module

    For example I have file external-function.js, combo.js and test.js on the same directory. I choose to use console.log to show that stub works and fake function get called, because you are not expecting something returned on myMethod.

    // File: external-function.js
    function externalFunction () {
      console.log('Original Called');
    }
    
    module.exports = { externalFunction };
    
    // File: combo.js
    // Note: "stubbed module can not be destructured."
    const externalFunction = require('./external-function')
    class Combo {
      constructor(props) {}
      async myMethod () {
        externalFunction.externalFunction()
      }
    }
    const myCombo = props => new Combo(props)
    module.exports = { myCombo };
    
    // File: test.js
    const sinon = require('sinon');
    const { myCombo } = require('./combo');
    const dependencyModule = require('./external-function');
    
    describe('myCombo', () => {
      it('myMethod', async () => {
        sinon.stub(dependencyModule, 'externalFunction').callsFake(() => {
          console.log('Fake Called');
        });
    
        const comboObj = myCombo({});
    
        await comboObj.myMethod();
      });
    });
    

    When I run it using nyc and mocha on my terminal:

    $ npx nyc mocha test.js
    
    
      myCombo
    Fake Called
        ✓ myMethod
    
    
      1 passing (3ms)
    
    ----------------------|---------|----------|---------|---------|-------------------
    File                  | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------------------|---------|----------|---------|---------|-------------------
    All files             |   85.71 |      100 |      75 |   83.33 |                   
     combo.js             |     100 |      100 |     100 |     100 |                   
     external-function.js |      50 |      100 |       0 |      50 | 2                 
    ----------------------|---------|----------|---------|---------|-------------------