How to change the mock implementation on a separate test basis [Jestjs]

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:

  1. dependency layout
  2. change / expand mock implementation in one test
  3. 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.


1 - mockFn.mockImplementationOnce (fn)

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' }); 

2 - jest.doMock (moduleName, factory, options)

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(); }); 

4 - jest.spyOn (object, methodName)

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? }); 
+32
source share
3 answers

A good template for writing a test is to create a settings factory function that returns the data needed to test the current module.

The following is an example code following your second example, although it does allow you to provide default values ​​and redefine values ​​again.

 const spyReturns = returnValue => jest.fn(() => returnValue); describe("scenario", () => { const setup = (mockOverrides) => { const mockedFunctions = { a: spyReturns(true), b: spyReturns(true), ...mockOverrides } return { mockedModule: jest.doMock('../myModule', () => mockedFunctions) } } it("should return true for module a", () => { const { mockedModule } = setup(); expect(mockedModule.a()).toEqual(true) }); it("should return override for module a", () => { const EXPECTED_VALUE = "override" const { mockedModule } = setup({ a: spyReturns(EXPECTED_VALUE)}); expect(mockedModule.a()).toEqual(EXPECTED_VALUE) }); }); 
+18
source

Use mockFn.mockImplementation (fn) .

Put the default implementation in beforeEach . The layout will be reset to this before each test.

To override, use mockImplementation in the test.

This will override the layout behavior for any / all calls in the test and will be overwritten by the beforeEach implementation before the next test.

For example:

 import { funcToMock } from './somewhere'; jest.mock('./somewhere'); beforeEach(() => { funcToMock.mockImplementation(() => { /* default implementation */ }); }); test('case that needs a different implementation of funcToMock', () => { funcToMock.mockImplementation(() => { /* implementation specific to this test */ }); // ... }); 
+7
source

A little late for the party, but if anyone else had problems with this.

We use TypeScript, ES6 and babel for quick development.

Usually we make fun of external __mocks__ root directory.

I wanted to override a specific module function in the Auth aws-ampify class for a specific test.

  import { Auth } from 'aws-amplify'; import GetJwtToken from './GetJwtToken'; ... it('When idToken should return "123"', async () => { const spy = jest.spyOn(Auth, 'currentSession').mockImplementation(() => ({ getIdToken: () => ({ getJwtToken: () => '123', }), })); const result = await GetJwtToken(); expect(result).toBe('123'); spy.mockRestore(); }); 

Gist: https://gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2

Manual: https://medium.com/p/b4ac52a005d#19c5

+6
source

All Articles