Python как функция макет в JavaScript - PullRequest
0 голосов
/ 29 августа 2018

С Python так легко смоделировать функцию, которая используется в тестируемой функции.

# 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')

Возможно ли что-то подобное в JavaScript с использованием, например, 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')
  });
});

Я прочитал https://github.com/facebook/jest/issues/936 и до сих пор не знаю, как это сделать, поскольку существует очень много (хакерских) предложений (некоторым из них ~ 2 года).

1 Ответ

0 голосов
/ 30 августа 2018

Jest может смоделировать весь модуль, используя jest.mock(), или смоделировать экспорт отдельного модуля, используя jest.spyOn() в сочетании с такими функциями, как mockImplementation().

Это позволяет легко смоделировать функцию, импортированную из библиотеки:

// ---- 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
  });
});

В примере кода из вопроса myModule содержит две функции, и одна вызывает другую напрямую.

Поскольку макет работает как с целым модулем, так и с экспортом модуля, имитировать прямой вызов myFunctionA() из myFunctionB() будет очень сложно при написании кода.

Самый простой способ, который я нашел для обхода подобных ситуаций, - импортировать модуль в себя и использовать модуль при вызове функции. Таким образом вызывается экспорт модуля, который можно смоделировать в тесте:

// ---- 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
  });
});
...