有了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岁(。
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
包含两个函数,一个函数直接调用另一个函数。
由于模拟适用于整个模块或模块导出,因此从myFunctionB()
内部模拟对myFunctionA()
的直接调用将非常困难。
我发现解决此类情况的最简单方法是将模块导入自身并在调用函数时使用模块。 这样,它就是被调用的模块导出,并且可以在测试中模拟它:
// ---- 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
});
});