Python like function mock in JavaScript

Viewed 1218

With Python it's so easy to mock a function that is used in the function under test.

# my_module.py
def function_a():
    return 'a'


def function_b():
    return function_a() + 'b'



# tests/test_my_module.py
from unittest import TestCase
from unittest.mock import patch

from my_module import function_b


class MyModuleTestCase(TestCase):
    @patch('my_module.function_a')
    def test_function_b(self, mock_function_a):
        mock_function_a.return_value = 'c'

        self.assertEqual(function_b(), 'cb')

Is something like this possible in JavaScript using, for example, jest?

# myModule.js
function myFunctionA() {
  return 'a';
}

export function myFunctionB() {
  return myFunctionA() + 'b';
}



# __test__/test.myModule.js
import { myFunctionB } from '../myModule';

describe('myModule tests', () => {
  test('myFunctionB works', () => {
    // Mock `myFunctionA` return value here somehow. 
    expect(myFunctionB()).toBe('cb')
  });
});

I've read https://github.com/facebook/jest/issues/936 and still have no idea how to do it as there are so many (hacky) suggestions (some of them ~2 years old).

1 Answers

Jest can mock an entire module using jest.mock() or mock an individual module export using jest.spyOn() in combination with functions like mockImplementation().

This makes it easy to mock a function imported from a library:

// ---- lib.js ----
export function myFunctionA() {
  return 'a';
}


// ---- myModule.js ----
import { myFunctionA } from './lib';

export function myFunctionB() {
  return myFunctionA() + 'b';  // call lib.myFunctionA()
}


// ---- myModule.test.js ----
import { myFunctionB } from './myModule';
import * as lib from './lib';

describe('myModule tests', () => {
  test('myFunctionB works', () => {
    const mock = jest.spyOn(lib, 'myFunctionA');  // create a spy on lib.myFunctionA()
    mock.mockImplementation(() => 'c');  // replace the implementation

    expect(myFunctionB()).toBe('cb');

    mock.mockRestore();  // remove the spy and mock implementation
  });
});

In the code sample from the question myModule contains two functions and one calls the other directly.

Because a mock works on either an entire module or a module export, mocking the direct call to myFunctionA() from within myFunctionB() would be very difficult the way the code is written.

The easiest way I have found to work around situations like this is to import the module into itself and use the module when calling the function. That way it is the module export that is being called and it can be mocked in the test:

// ---- myModule.js ----
import * as myModule from './myModule';

export function myFunctionA() {
  return 'a';
}

export function myFunctionB() {
  return myModule.myFunctionA() + 'b';  // call myModule.myFunctionA()
}


// ---- myModule.test.js ----
import * as myModule from './myModule';

describe('myModule tests', () => {
  test('myFunctionB works', () => {
    const mock = jest.spyOn(myModule, 'myFunctionA');  // create a spy on myModule.myFunctionA()
    mock.mockImplementation(() => 'c');  // replace the implementation

    expect(myModule.myFunctionB()).toBe('cb');

    mock.mockRestore();  // remove the spy and mock implementation
  });
});
Related