Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I only want to mock a single function (named export) from a module but leave the rest of the module functions intact.

Using jest.mock('package-name') makes all exported functions mocks, which I don't want.

I tried spreading the named exports back into the mock object...

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

jest.mock(utils, () => ({
  ...utils
  speak: jest.fn(),
}));

but got this error:

The module factory of jest.mock() is not allowed to reference any out-of-scope variables.

like image 942
spencer.sm Avatar asked Dec 12 '19 21:12

spencer.sm


People also ask

How do you mock only one function from a module in Jest?

Another approach to mock a particular function from an imported module is to use the jest. spyOn function. The API for this function seems to be exactly what we need for our use case, as it accepts an entire module and the particular export that should be spied on.

How do you mock a particular function in Jest?

For example, you have two functions in a module file. When you are trying to write your jest unit test to test the function greet(), you would assume that you can just mock the getFullName() inside function greet() . getFullName = jest. fn() .

How do you reset mock Jest?

mockClear to clear the mocked local. getData method after each test by calling it in the afterEach callback. afterEach(() => { jest. clearAllMocks(); });

Does Jest automatically clear mocks?

Configuring Jest to automatically reset / restore mocks and spies. As it seemed, it turned out Jest can be configured to do an automatic reset / restore before executing each unit test spec. Is effectively the same as: beforeEach(() => { jest.


4 Answers

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

like image 101
Popsicle Avatar answered Oct 07 '22 05:10

Popsicle


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

like image 25
spencer.sm Avatar answered Oct 07 '22 06:10

spencer.sm


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]
        }
      }
    },
  })
})
like image 9
Rico Kahler Avatar answered Oct 07 '22 05:10

Rico Kahler


For me this worked:

const utils = require('./utilities.js');
...
jest.spyOn(utils, 'speak').mockImplementation(() => jest.fn());
like image 8
kkl Avatar answered Oct 07 '22 05:10

kkl