我想改变嘲笑依赖的实施在每个单一的测试基础上通过扩展默认模仿的行为,并恢复回原来的执行下一个测试执行时。
更简单地说,这就是我要实现的目标:
- 模拟依赖
- 在单个测试中更改/扩展模拟实现
- 下一次测试执行时恢复为原始模拟
我目前正在使用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-模拟Fn.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方法(如解释在这里)
优点
- 完全控制模拟结果
缺点
- 很多样板代码
- 长期难以维持
码:
__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(对象,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?
});