Search code examples
javascriptunit-testingaxiosjestjs

How can I disable a jest mock for a particular test?


I have created a working Mock for Axios:

// __mocks__/axios.js
// Based on https://jestjs.io/docs/manual-mocks

const axios = jest.createMockFromModule("axios");
const log = console.log.bind(console);

axios.create = () => {
  log(`Running axios.create`);
  return {
    get: () => {
      log(`Running get`);
      return {
        status: 500,
        statusText: "Internal Server Error",
        body: {
          onFire: "Mock API response from mock axios module",
        },
      };
    },
  };
};

module.exports = axios;

This works fine in my tests - the mock is loaded automatically and the 'throws an error' test works:

describe(`getLatestPrice`, () => {
  it(`throws an error when the response is bad`, async () => {
    expect(() => {
      log(`Should throw`);
      return getLatestPrice(assetCode);
    }).toThrow();
  });

  it(`gets a single price by stream code`, async () => {
    // Disabling the mock isn't working
    jest.unmock("axios");
    const price = await getLatestPrice(assetCode);
    log(`price`, price);
    expect(price).toEqual({
      ...
    });
  });
})

However the second test - which calls jest.unmock() - still uses the mocked library.

How can I disable mocking for a single test?

Update: reading https://github.com/facebook/jest/issues/2649 I've also tried using requireActual() to override the mock:

const actualAxios = jest.requireActual("axios");
const mockAxios = require("axios");
mockAxios.create = actualAxios.create;

But calls to axios.create() still invole the mock.


Solution

  • I had a similar problem with mocking useSelector, which I wanted to behave normally in other tests. The only thing that eventually worked was to mock mocked useSelector with the actual useSelector. So first make sure to have way to access the actual module:

    import {useSelector as actualUseSelector} from "react-redux"
    import {useSelector} from "react-redux";
    

    then I've done my mocking

    jest.mock('react-redux', () => ({
      ...jest.requireActual('react-redux'),
      useSelector: jest.fn()
    }));
    

    inside tests that needed modified redux state added

    useSelector.mockImplementation(callback => {
      return callback({
        sliceA: {...initialStateSliceA},
        sliceB: {...initialStateSliceB},
        sliceC: {...initialStateSliceC, importantData: [{d: 1}, {d: 2}]}
      })
    })
    

    and then in tests where I needed original useSelector

    useSelector.mockImplementation(()=>actualUseSelector)
    

    and it worked!

    UPDATE

    Actually the above solution doesn't work as intended, it worked in one case, but for wrong reasons. It was still a mock function (and if you think about it, it makes sense). But eventually I found a working solution:

    you have to recreate the whole redux anew:

    const actualRedux = jest.requireActual('react-redux')
    

    and only then mock useSelector or useDispatch with the actual ones:

    useSelector.mockImplementation(actualRedux.useSelector)
    useDispatch.mockImplementation(actualRedux.useDispatch)