我想在每个测试基础上更改模拟依赖的实现,方法是扩展默认模拟的行为,并在下一次测试执行时将其恢复到原始实现。
更简单地说,这就是我想要达到的目标:
模拟的依赖 在单个测试中更改/扩展模拟实现 在执行下一次测试时恢复到原始模拟
我目前使用的是Jest v21。下面是一个典型的测试:
// __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
});
});
以下是我目前为止尝试过的方法:
mockFn.mockImplementationOnce(fn) it('should override myModule.b mock result (and leave the other methods untouched)', () => { myMockedModule.b.mockImplementationOnce(() => 'overridden'); myModule.a(); // === true myModule.b(); // === 'overridden' }); Pros Reverts back to original implementation after first call Cons It breaks if the test calls b multiple times It doesn't revert to original implementation until b is not called (leaking out in the next test) 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' }); Pros Explicitly re-mocks on every test Cons Cannot define default mock implementation for all tests Cannot extend default implementation forcing to re-declare each mocked method Manual mocking with setter methods (as explained 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(); }); Pros Full control over mocked results Cons Lot of boilerplate code Hard to maintain on long term jest.spyOn(object, methodName) 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? }); Cons I can't revert mockImplementation back to the original mocked return value, therefore affecting the next tests