Jest 模拟内部函数
Posted
技术标签:
【中文标题】Jest 模拟内部函数【英文标题】:Jest mock inner function 【发布时间】:2018-12-18 13:08:26 【问题描述】:我有一个名为 helper.js 的文件,其中包含两个函数
export const funcA = (key) =>
return funcB(key)
;
export const funcB = (key,prop) =>
return someObj;
;
我有我的 helper.spec.js 来测试 helper.js 文件的功能。
import funcA,funcB from 'helper';
describe('helper', () =>
test('testFuncB', () =>
test('testFuncA', () =>
funcB 的测试非常简单,我只是调用它并期待 someObj 问题是测试funcA,为了测试它我想模拟funcB的响应。
我希望 testFuncB 调用实际的 funcB 和 testFuncA 调用模拟的 funcB
如何在我的两个测试中实现 funcB 被模拟和原创?
这不是重复的。这是一个不同的情况:他们只模拟内部调用的函数,如果我删除 testFuncB 那么它将是相同的,但我也必须对 testFuncB 执行测试。
【问题讨论】:
***.com/questions/49942866/… Mocking a function call inside a function in Jest的可能重复 答案只是我问题的一部分。更新了问题。 你可以尝试只模拟 funcB inside testFuncA。 我该怎么做?有代码示例吗? 【参考方案1】:如果一个 ES6 模块直接导出两个函数(不是在类、对象等内,只是直接导出问题中的函数)并且一个直接调用另一个,那么 该调用不能被模拟强>。
在这种情况下,funcB
不能在funcA
内模拟,就像代码当前的编写方式一样。
模拟替换了funcB
的模块导出,但funcA
不调用funcB
的模块导出,它只是调用funcB
直接。
在funcA
中模拟funcB
需要funcA
为funcB
调用模块导出。
这可以通过以下两种方式之一完成:
将 funcB
移动到自己的模块中
funcB.js
export const funcB = () =>
return 'original';
;
helper.js
import funcB from './funcB';
export const funcA = () =>
return funcB();
;
helper.spec.js
import * as funcBModule from './funcB';
import funcA from './helper';
describe('helper', () =>
test('test funcB', () =>
expect(funcBModule.funcB()).toBe('original'); // Success!
);
test('test funcA', () =>
const spy = jest.spyOn(funcBModule, 'funcB');
spy.mockReturnValue('mocked');
expect(funcA()).toBe('mocked'); // Success!
spy.mockRestore();
);
);
将模块导入自身
"ES6 modules support cyclic dependencies automatically" 所以将import
一个模块插入自身是完全有效的,这样模块内的函数就可以为模块中的其他函数调用模块导出:
helper.js
import * as helper from './helper';
export const funcA = () =>
return helper.funcB();
;
export const funcB = () =>
return 'original';
;
helper.spec.js
import * as helper from './helper';
describe('helper', () =>
test('test funcB', () =>
expect(helper.funcB()).toBe('original'); // Success!
);
test('test funcA', () =>
const spy = jest.spyOn(helper, 'funcB');
spy.mockReturnValue('mocked');
expect(helper.funcA()).toBe('mocked'); // Success!
spy.mockRestore();
);
);
【讨论】:
收到(function (exports, require, module, __filename, __dirname) import
怎么办?
@Prakhar 你需要 compile with something like Babel 因为 Node.js 支持 ES6 模块 is still just experimental
@Prakhar 在上面的例子中使用exports.funcB(key)
里面的funcA
@BrianAdams 我如何只监视嵌套的 1 个特定函数;通过第二种方法?例如:funcA 调用 funcB, funcC, funcD, 和 funcC 依次调用 funcCC 进而调用 funcCCC,我想通过模拟 funcCCC 来为 funcA 编写测试
@Ganga funcCCC
只需要导出,funcCC
需要通过模块调用。如果funcCC
使用模块调用funcCCC
,则可以在测试期间模拟funcCCC
的模块导出。【参考方案2】:
迟到的答案,但这应该有效。 此外,您应该在自己的文件中测试 funcB,而不是在“助手”测试中。
import funcB from './funcB';
import funcA from './helper';
jest.mock('./funcB');
describe('helper', () =>
test('test funcA', () =>
const funcBSpy = jest.fn();
funcB.mockImplementation(() => funcBSpy());
funcA();
expect(funcBSpy).toHaveBeenCalledTimes(1);
);
);
【讨论】:
如果 funcA 不是 funcB 怎么调用? @catch22 感谢您指出 :) 更新了代码 :)【参考方案3】:import * as helper from 'helper';
describe('helper', () =>
it('should test testFuncA', () =>
const mockTestFuncB = jest.mock();
// spy on calls to testFuncB and respond with a mock function
mockTestFuncB.spyOn(helper, 'testFuncB').mockReturnValue(/*your expected return value*/);
// test logic
// Restore helper.testFuncB to it's original function
helper.testFuncB.mockRestore();
【讨论】:
【参考方案4】:我创建了一种命名空间来处理这个问题:
let helper =
const funcA = (key) =>
return helper.funcB(key)
;
const funcB = (key,prop) =>
return someObj;
;
helper = funcA, funcB
module.exports = helper
然后使用 jest.fn
进行模拟很明显
【讨论】:
【参考方案5】:您可以在测试funcA
时执行以下技巧:
1.模拟funcB
:
helper.funcB = jest.fn().mockImplementationOnce(() => <your data>);
2.将funcB(key)
改为this.funcB(key)
我遇到了同样的问题并且工作了!完整代码:
export const funcA = (key) =>
return this.funcB(key)
;
export const funcB = (key,prop) =>
return someObj;
;
测试代码:
import helper from 'helper';
describe('helper', () =>
test('testFuncB', () =>
...
test('testFuncA', () =>
helper.funcB = jest.fn().mockImplementationOnce(() => <your data>);
【讨论】:
【参考方案6】:你可以使用babel-plugin-rewire提供的__set__
函数来模拟内部函数。
假设你已经设置了 babel-plugin-rewire。
helper.spec.js
import funcA, __set__ as helper from './helper';
describe('helper', () =>
test('test funcA', () =>
__set__('funcB', () =>
return 'funcB return value'
)
expect(funcA()).toBe('funcB return value');
);
);
此解决方案的一个优点是您无需更改任何原始代码
【讨论】:
【参考方案7】:我认为这可能有效
import * as helper from 'helper';
describe('helper', () =>
test('testFuncB', () =>
test('testFuncA', () =>
const mockTestFuncB = jest.mock();
// spy on calls to testFuncB and respond with a mock function
jest.spyOn(helper, 'testFuncB').mockImplementationOnce(mockTestFuncB);
// Do the testing ...
// Restore helper.testFuncB to it's original function
helper.testFuncB.mockRestore();
【讨论】:
这个问题的新观众注意:这不起作用。有关解释和工作示例,请参阅 this answer。以上是关于Jest 模拟内部函数的主要内容,如果未能解决你的问题,请参考以下文章
如何在构造函数内部使用上下文的情况下使用 jest 和酶来测试反应上下文?