【发布时间】:2018-07-25 05:55:36
【问题描述】:
我想通过扩展默认模拟的行为更改模拟依赖项的实现,以每个单个测试为基础并在下一个测试执行时将其恢复为原始实现。
简而言之,这是我想要实现的目标:
- 模拟依赖
- 在单个测试中更改/扩展模拟实现
- 在下次测试执行时恢复原始模拟
我目前正在使用Jest v21。
这是典型的 Jest 测试的样子:
__mocks__/myModule.js
const myMockedModule = jest.genMockFromModule('../myModule');
myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);
export default myMockedModule;
__tests__/myTest.js
import myMockedModule from '../myModule';
// Mock myModule
jest.mock('../myModule');
beforeEach(() => {
jest.clearAllMocks();
});
describe('MyTest', () => {
it('should test with default mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
// Extend change mock
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// Restore mock to original implementation with no side effects
});
it('should revert back to default myMockedModule mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
});
这是我迄今为止尝试过的:
1 - mockFn.mockImplementationOnce(fn)
专业人士
- 第一次调用后恢复到原始实现
缺点
- 如果测试多次调用
b就会中断 - 在未调用
b之前,它不会恢复到原始实现(在下一个测试中泄漏)
代码:
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myMockedModule.b.mockImplementationOnce(() => 'overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
2 - jest.doMock(moduleName, factory, options)
专业人士
- 明确地重新模拟每个测试
缺点
- 无法为所有测试定义默认模拟实现
- 无法扩展默认实现强制重新声明每个模拟 方法
代码:
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
jest.doMock('../myModule', () => {
return {
a: jest.fn(() => true,
b: jest.fn(() => 'overridden',
}
});
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
3 - 使用 setter 方法手动模拟(如 here 所述)
专业人士
- 完全控制模拟结果
缺点
- 大量样板代码
- 难以长期维护
代码:
__mocks__/myModule.js
const myMockedModule = jest.genMockFromModule('../myModule');
let a = true;
let b = true;
myMockedModule.a = jest.fn(() => a);
myMockedModule.b = jest.fn(() => b);
myMockedModule.__setA = (value) => { a = value };
myMockedModule.__setB = (value) => { b = value };
myMockedModule.__reset = () => {
a = true;
b = true;
};
export default myMockedModule;
__tests__/myTest.js
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myModule.__setB('overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
myModule.__reset();
});
4 - jest.spyOn(object, methodName)
缺点
- 我无法将
mockImplementation恢复为原始模拟返回值,因此会影响接下来的测试
代码:
beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
// Mock myModule
jest.mock('../myModule');
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden');
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// How to get back to original mocked value?
});
【问题讨论】:
-
不错。但是你如何为像'@private-repo/module'这样的npm模块做选项2?我看到的大多数例子都有相对路径?这也适用于已安装的模块吗?
标签: javascript unit-testing mocking jestjs