Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change mock implementation on a per single test basis [Jestjs]

People also ask

How do I change my mock Jest?

To change mock implementation for a single test with Jest, we can call the mockImplementation method on the function we want to mock in our test. import { funcToMock } from './module'; jest. mock('./module'); beforeEach(() => { funcToMock.

How is mock function implemented?

There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a manual mock to override a module dependency.

Does Jest automatically clear mocks?

This is useful when you want to completely reset a mock back to its initial state. (Note that resetting a spy will result in a function with no return value). The mockReset configuration option is available to reset mocks automatically before each test.

Can I use Jest mock inside a test?

mock() doesn't work inside tests, only outside tests. Bookmark this question.


A nice pattern for writing tests is to create a setup factory function that returns the data you need for testing the current module.

Below is some sample code following your second example although allows the provision of default and override values in a reusable way.


const spyReturns = returnValue => jest.fn(() => returnValue);

describe("scenario", () => {
  beforeEach(() => {
    jest.resetModules();
  });

  const setup = (mockOverrides) => {
    const mockedFunctions =  {
      a: spyReturns(true),
      b: spyReturns(true),
      ...mockOverrides
    }
    jest.doMock('../myModule', () => mockedFunctions)
    return {
      mockedModule: require('../myModule')
    }
  }

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

It's important to say that you must reset modules that have been cached using jest.resetModules(). This can be done in beforeEach or a similar teardown function.

See jest object documentation for more info: https://jestjs.io/docs/jest-object.


Use mockFn.mockImplementation(fn).

import { funcToMock } from './somewhere';
jest.mock('./somewhere');

beforeEach(() => {
  funcToMock.mockImplementation(() => { /* default implementation */ });
  // (funcToMock as jest.Mock)... in TS
});

test('case that needs a different implementation of funcToMock', () => {
  funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
  // (funcToMock as jest.Mock)... in TS

  // ...
});

Little late to the party, but if someone else is having issues with this.

We use TypeScript, ES6 and babel for react-native development.

We usually mock external NPM modules in the root __mocks__ directory.

I wanted to override a specific function of a module in the Auth class of aws-amplify 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

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


When mocking a single method (when it's required to leave the rest of a class/module implementation intact) I discovered the following approach to be helpful to reset any implementation tweaks from individual tests.

I found this approach to be the concisest one, with no need to jest.mock something at the beginning of the file etc. You need just the code you see below to mock MyClass.methodName. Another advantage is that by default spyOn keeps the original method implementation but also saves all the stats (# of calls, arguments, results etc.) to test against, and keeping the default implementation is a must in some cases. So you have the flexibility to keep the default implementation or to change it with a simple addition of .mockImplementation as mentioned in the code below.

The code is in Typescript with comments highlighting the difference for JS (the difference is in one line, to be precise). Tested with Jest 26.6.

describe('test set', () => {
    let mockedFn: jest.SpyInstance<void>; // void is the return value of the mocked function, change as necessary
    // For plain JS use just: let mockedFn;

    beforeEach(() => {
        mockedFn = jest.spyOn(MyClass.prototype, 'methodName');
        // Use the following instead if you need not to just spy but also to replace the default method implementation:
        // mockedFn = jest.spyOn(MyClass.prototype, 'methodName').mockImplementation(() => {/*custom implementation*/});
    });

    afterEach(() => {
        // Reset to the original method implementation (non-mocked) and clear all the mock data
        mockedFn.mockRestore();
    });

    it('does first thing', () => {
        /* Test with the default mock implementation */
    });

    it('does second thing', () => {
        mockedFn.mockImplementation(() => {/*custom implementation just for this test*/});
        /* Test utilising this custom mock implementation. It is reset after the test. */
    });

    it('does third thing', () => {
        /* Another test with the default mock implementation */
    });
});