I would like to change the implementation of a false dependency based on one test , extending the standard default behavior and returning it back to the original implementation when the next test is executed.
In short, this is what I'm trying to achieve:
- dependency layout
- change / expand mock implementation in one test
- return to the original layout during the next test
I am currently using Jest v21 .
Here's what a typical Jest test would look like:
__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 }); });
I tried several strategies, but did not find a solution that could determine a satisfying one.
pros
- Returns to the original implementation after the first call
minuses
- It will break if the test calls
b again - This will not return to the original implementation until
b is called (leaks into the next test)
The code:
it('should override myModule.b mock result (and leave the other methods untouched)', () => { myMockedModule.b.mockImplementationOnce(() => 'overridden'); myModule.a(); // === true myModule.b(); // === 'overridden' });
pros
- Explicitly double-checked on every test
minuses
- Failed to determine default mock implementation for all tests
- You cannot extend the default implementation by forcing to re-declare each layout method
The code:
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 - Manual mockery using installation methods (as described here )
pros
- Full control over fake results
minuses
- Template code lot
- Hard to maintain in the long run
The code:
__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(); });
minuses
- I cannot return
mockImplementation back to the original false return value, so it affects the following tests
The code:
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 the original mocked value? });