Mock only one function from module but leave rest with original functionality

Solution 1:

The highlight of this answer is jest.requireActual(), this is a very useful utility that says to jest that "Hey keep every original functionalities intact and import them".

jest.mock('./utilities.js', () => ({
  ...jest.requireActual('./utilities.js'),
  speak: jest.fn(),
}));

Let's take another common scenario, you're using enzyme ShallowWrapper and it doesn't goes well with useContext() hook, so what're you gonna do? While i'm sure there are multiple ways, but this is the one I like:

import React from "react";

jest.mock("react", () => ({
  ...jest.requireActual("react"), // import and retain the original functionalities
  useContext: jest.fn().mockReturnValue({foo: 'bar'}) // overwrite useContext
}))

The perk of doing it this way is that you can still use import React, { useContext } from "react" in your original code without worrying about converting them into React.useContext() as you would if you're using jest.spyOn(React, 'useContext')

Solution 2:

The most straightforward way is to use jest.spyOn and then .mockImplementation(). This will allow all other functions in the module to continue working how they're defined.

For packages:

import axios from 'axios';

jest.spyOn(axios, 'get');
axios.get.mockImplementation(() => { /* do thing */ });

For modules with named exports:

import * as utils from './utilities.js';

jest.spyOn(utils, 'speak');
utils.speak.mockImplementation(() => { /* do thing */ });

Docs here: https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname

Solution 3:

For me this worked:

const utils = require('./utilities.js');
...
jest.spyOn(utils, 'speak').mockImplementation(() => jest.fn());

Solution 4:

jest.requireActual inside of jest.mock seems like the way to go, however I needed to add a proxy instead of the object spread to prevent the type error Cannot read properties of undefined (reading ...) which can occur in certain import scenarios.

This is the final result:

jest.mock('the-module-to-mock', () => {
  const actualModule = jest.requireActual('the-module-to-mock')

  return new Proxy(actualModule, {
    get: (target, property) => {
      switch (property) {
        // add cases for exports you want to mock
        // 👇👇👇
        case 'foo': {
          return jest.fn() // add `mockImplementation` etc
        }
        case 'bar': {
          return jest.fn()
        }
        // fallback to the original module
        default: {
          return target[property]
        }
      }
    },
  })
})