How to mock imported functions with Jest (2024)

📣 Notice
This article is part of a series:

Testing JavaScript with Jest

To get the most out of this article, I recommend reading the previous article in the series:

How to write unit tests in JavaScript with Jest

Writing unit tests for code with dependencies can be difficult. This is especially true for large code bases where it's tricky to come up with fixtures that will cover all the cases we need to test.

But what if we could control the return value of a function dependency, no matter what arguments it is called with?

This is where mock functions come in.

Mock functions are a testing tool that allows us to track how function dependencies are called and control their return values. This makes it possible for us to manipulate the control flow of the tested program and reach even those difficult-to-reproduce edge-cases when writing tests.

This article will provide an introduction into the concepts behind mocking and how it relates to unit testing. We will learn how to mock functions and imported function modules with Jest, and write tests that rely on those mocks to increase the coverage of our test cases.

We will assume that we're testing a couple of validation rules:

// isInteger.jsmodule.exports = (value) => Number.isSafeInteger(value);

// isAtLeast18.jsconst isInteger = require("./isInteger");module.exports = (value) => isInteger(value) && value >= 18;

We want to see what our tests will teach us about the flaws in our code by passing and failing test cases. Fixing the implementation is not covered by this article, but feel free to play with it as we move through the article.

Read on to find out more!

How to mock an imported function with Jest?

To mock an imported function with Jest we use the jest.mock() function.

jest.mock() is called with one required argument - the import path of the module we're mocking. It can also be called with an optional second argument - the factory function for the mock. If the factory function is not provided, Jest will automock the imported module.

💡Note
Jest automock is the automatic mocking of imported modules with surface-level replacement implementations. Automocking is disabled by default since Jest 15, but can be enabled by configuring Jest with the automock flag.

When testing isAtLeast18() we have to keep in mind that the isInteger() dependency affects the module's behaviour:

  1. if isInteger() is false , isAtLeast18() is false;
  2. if isInteger() is true , isAtLeast18() depends on the value argument.

We'll start by testing the case of isInteger() returning false.

The isInteger.js module has a single default export - the isInteger() function. We will mock the imported module with a factory function that behaves just like the default export and returns a function. That function, when called, will always return false.

// isAtLeast18.spec.jsconst isAtLeast18 = require("./isAtLeast18");// The mock factory returns the function () => falsejest.mock("./isInteger", () => () => false);describe("isAtLeast18", () => { it("fails if value is not recognised as integer", () => { // Should pass, but fails because of the isInteger() mock expect(isAtLeast18(123)).toBe(false); // Should fail either way expect(isAtLeast18("abc")).toBe(false); });});

💡Note
The import path of the mocked module must match the import path that is present in the module we're testing. The isAtLeast18.js module imports the isInteger.js module under the path "./isInteger". This is why our mock import path is also "./isInteger".

isAtLeast18() will now always return false no matter what we call it with, because the isInteger() mock is set to always return false.

But what about the case when isInteger() returns true?

To mock different return values depending on the test we will create a mock function.

💡Note
This unit test is a solitary unit test because the tested unit is isolated from its dependencies. Read more about solitary unit tests in the previous article: How to write unit tests in JavaScript with Jest.

What is a mock function?

A mock function is a function that replaces the actual implementation of a function with a "fake" (mock) implementation.

Mock functions track how they are called by external code. With a mock function we can know the number of times the function was called, the arguments it was called with, the result it returned, and more. This ability to "spy" on function calls is why mock functions are also called spies.

We use mock functions to override original function behaviour with custom mock implementations. Mock implementations help us control the return values of a function. This makes our tests more predictable (deterministic) and easier to write.

How to mock a function with Jest?

To mock a function with Jest we use the jest.fn() function.

jest.fn() can be called with an implementation function as an optional argument. If an implementation is provided, calling the mock function will call the implementation and return it's return value.

If no implementation is provided, calling the mock returns undefined because the return value is not defined.

// Without implementation, this mock returns `undefined`.const mockUndefined = jest.fn();// With implementation, this mock returns `true`.const mockTrue = jest.fn(() => true).

Jest registers mock functions under the "jest.fn()" name by default. We can give the mock function a custom name with the mockName() method. The mock name is used when printing test results.

const mockOne = jest.fn(() => false);// Example error: expect(jest.fn()).toHaveBeenCalledWith(...expected)const mockTwo = jest.fn(() => false).mockName('mockTwo');// Example error: expect(mockTwo).toHaveBeenCalledWith(...expected)

💡Note
It's good practice to name mocked functions in cases where a lot of different mocks are used. This makes it easier to tell mocked functions apart and debug code that isn't matching expectations.

How to change the mock implementation of a function with Jest?

To change the mock implementation of a function with Jest we use the mockImplementation() method of the mocked function.

The mockImplementation() method is called with the new implementation as its argument. The new implementation will then be used in place of the previous one when the mock is called.

// The initial mock is a function that returns `true`.const myMock = jest.fn(() => true);// The new mock implementation has the function return `false`.myMock.mockImplementation(() => false);

We can combine this with jest.mock() factory functions to create mocked modules that contain mocked functions. This way we can control how the implementation of a mock behaves depending on what we're testing.

// isAtLeast18.spec.jsconst isAtLeast18 = require("./isAtLeast18");const isInteger = require("./isInteger");// The mock factory returns a mocked functionjest.mock("./isInteger", () => jest.fn());describe("isAtLeast18", () => { it("fails if value is not recognised as integer", () => { // For this test we'll mock isInteger to return `false` isInteger.mockImplementation(() => false); expect(isAtLeast18(123)).toBe(false); expect(isAtLeast18("abc")).toBe(false); }); it("passes if value is recognised as integer and is at least 18", () => { // For this test we'll mock isInteger to return `true` isInteger.mockImplementation(() => true); expect(isAtLeast18(123)).toBe(true); expect(isAtLeast18("abc")).toBe(false); });});

💡Note
jest.mock() works by modifying the Node module cache to give us the mock instead of the original implementation whenever we import a mocked module in a test file. To support ES module imports - where import statements have to come first in a file - Jest automatically hoists jest.mock() calls to the top of the module. Read more about this technique here.

How to check if a function was called correctly with Jest?

To check if a function was called correctly with Jest we use the expect() function with specific matcher methods to create an assertion.

We can use the toHaveBeenCalledWith() matcher method to assert the arguments the mocked function has been called with.

To assert how many times the mocked function has been called so far, we can use the toHaveBeenCalledTimes() matcher method.

// isAtLeast18.spec.jsconst isAtLeast18 = require("./isAtLeast18");const isInteger = require("./isInteger");jest.mock("./isInteger", () => jest.fn());describe("isAtLeast18", () => { it("fails if value is not recognised as integer", () => { isInteger.mockImplementation(() => false); expect(isAtLeast18(123)).toBe(false); // We expect isInteger to be called with 123 expect(isInteger).toHaveBeenCalledWith(123); // We expect isInteger to be called once expect(isInteger).toHaveBeenCalledTimes(1); });});

💡Note
While these are the most common matcher methods for functions, there are more matcher methods available in the Jest API docs.

Jest tracks all calls to mocked functions. A mocked function will remember the arguments and times it has been called, as well as the results of those calls.

When reusing mocked functions between tests it is useful to reset their states before running new tests to get a clear baseline. We can do that by clearing mocked functions between tests.

How to clear mocked functions with Jest?

To clear mocked functions with Jest we use the mockClear() method of a mocked function.

mockClear() resets all information stored in mocked functions which makes it useful for cleaning up a mock's usage data between assertions or tests.

// isAtLeast18.spec.jsconst isAtLeast18 = require("./isAtLeast18");const isInteger = require("./isInteger");jest.mock("./isInteger", () => jest.fn());describe("isAtLeast18", () => { it("fails if value is not recognised as integer", () => { isInteger.mockImplementation(() => false); expect(isAtLeast18(123)).toBe(false); expect(isInteger).toHaveBeenCalledWith(123); expect(isInteger).toHaveBeenCalledTimes(1); // Clear the mock so the next test starts with fresh data isInteger.mockClear(); }); it("passes if value is recognised as integer and is at least 18", () => { isInteger.mockImplementation(() => true); expect(isAtLeast18(123)).toBe(true); expect(isInteger).toHaveBeenCalledWith(123); // Without clearing, there would be 2 calls total at this point expect(isInteger).toHaveBeenCalledTimes(1); });});

💡Note
Jest also provides mock function methods for resetting and restoring mocked functions, as well as shorthands for creating mocked functions that directly return, resolve, or reject a value.

How to clear mocked functions before each test with Jest?

To clear mocked functions before each test with Jest we use the beforeEach() function.

beforeEach() is called with one required argument - the function to run before each of the tests in the test file. We use it to clear mocks, set up fixtures, or reset some other state used across tests.

// isAtLeast18.spec.jsconst isAtLeast18 = require("./isAtLeast18");const isInteger = require("./isInteger");jest.mock("./isInteger", () => jest.fn());// Clear mock data before each testbeforeEach(() => { isInteger.mockClear();});describe("isAtLeast18", () => { it("fails if value is not recognised as integer", () => { isInteger.mockImplementation(() => false); expect(isAtLeast18(123)).toBe(false); expect(isInteger).toHaveBeenCalledWith(123); expect(isInteger).toHaveBeenCalledTimes(1); }); it("passes if value is recognised as integer and is at least 18", () => { isInteger.mockImplementation(() => true); expect(isAtLeast18(123)).toBe(true); expect(isInteger).toHaveBeenCalledWith(123); expect(isInteger).toHaveBeenCalledTimes(1); });});

💡Note
Jest provides four functions to hook into the set-up and tear-down process, both before and after each or all of the tests in a test file. These functions are: afterAll(), afterEach(), beforeAll(), beforeEach(). The afterAll() and beforeAll() variants are called only once for the entire test file. The afterEach() and beforeEach() variants are called once for every test in the test file.

How to reuse mocks with Jest?

To reuse mocks with Jest we create mocks in a __mocks__/ subdirectory adjacent to the module we want to mock.

Mock files in the __mocks__/ subdirectory are used to automock the modules they are adjacent to when the module is mocked with jest.mock(). This is useful when dealing with a lot of repetition in setting up mocks such as when mocking common dependencies or configuration objects because it makes writing a mock factory function unnecessary.

Assuming a common configuration file that is used by many different modules, mocking it would look like this:

// common/config.jsmodule.exports = { foo: "bar" };

// common/__mocks__/config.jsmodule.exports = { foo: "mockBar" };

// example.jsconst config = require.("./common/config");// Logs "bar"module.exports = () => console.log(config.foo);

// example.spec.jsconst example = require("./example");jest.mock("./common/config");// Logs "mockBar", no need for a mock factoryexample();

💡Note
Remember: mocks and automocking are only in effect when running tests with Jest. They do not have an effect on the code in development or production.

That's it! We're now ready to mock imported functions with Jest.

Jest mock imported functions example code

The dependency in isInteger.js:

// isInteger.jsmodule.exports = (value) => Number.isSafeInteger(value);

The unit to be tested in isAtLeast18.js:

// isAtLeast18.jsconst isInteger = require("./isInteger");module.exports = (value) => isInteger(value) && value >= 18;

The unit test in isAtLeast18.spec.js:

// isAtLeast18.spec.jsconst isAtLeast18 = require("./isAtLeast18");const isInteger = require("./isInteger");// The mock factory returns a mocked functionjest.mock("./isInteger", () => jest.fn());beforeEach(() => { isInteger.mockClear();});describe("isAtLeast18", () => { it("fails if value is not recognised as integer", () => { isInteger.mockImplementation(() => false); expect(isAtLeast18(123)).toBe(false); expect(isInteger).toHaveBeenCalledWith(123); expect(isInteger).toHaveBeenCalledTimes(1); }); it("passes if value is recognised as integer and is at least 18", () => { isInteger.mockImplementation(() => true); expect(isAtLeast18(123)).toBe(true); expect(isInteger).toHaveBeenCalledWith(123); expect(isInteger).toHaveBeenCalledTimes(1); });});

Homework and next steps

  • Write more comprehensive tests and use fixtures to cover any additional cases. If you've done your homework from the previous article, try continuing from where you left off.
  • Fix the code so any failed tests pass or write a newer, better implementation.
  • Achieve 100% code coverage in the coverage report.

Thank you for taking the time to read through this article!

Have you tried mocking imported functions with Jest before? What was your experience like?

Leave a comment and start a discussion!

How to mock imported functions with Jest (2024)

FAQs

How to mock import component Jest? ›

First, to mock a component, you use jest. mock("path/to/RealComponent") . You can specify an mock implementation inline like jest. mock("../src/Icon" () => { ... }) .

How to mock a standalone function in Jest? ›

You can optionally provide a name for your mock functions, which will be displayed instead of 'jest. fn()' in the test error output. Use . mockName() if you want to be able to quickly identify the mock function reporting an error in your test output.

How to mock a function globally in Jest? ›

When mocking global object methods in Jest, the optimal way to do so is using the jest.spyOn() method. It takes the object and name of the method you want to mock, and returns a mock function. The resulting mock function can then be chained to a mocked implementation or a mocked return value.

How to mock a function in a class Jest? ›

Calling jest.mock() with the module factory parameter

A module factory is a function that returns the mock. In order to mock a constructor function, the module factory must return a constructor function. In other words, the module factory must be a function that returns a function - a higher-order function (HOF).

How to test a function in Jest? ›

To test if a function is called based on a condition in Jest, you can use Jest's built-in mock functions and assertions. Here is an example: In this example, we create a mock function using Jest's jest. fn() method.

Can you use import in jest? ›

The jest object is automatically in scope within every test file. The methods in the jest object help create mocks and let you control Jest's overall behavior. It can also be imported explicitly by via import {jest} from '@jest/globals' .

How to mock methods using jest? ›

To mock an object in Jest, use the jest. mock() function with the path to the module you want to mock. You can then define a mock implementation for the object's methods and properties using jest. fn().

How to test if mock function is called in Jest? ›

Jest gives us different methods to make sure the mock function was called the way we expected. For example, we can use toHaveBeenCalled to check if it was called at all, toHaveBeenCalledWith to check the specific arguments it was called with, and toHaveBeenCalledTimes to check how many times it was called.

How to test exported function Jest? ›

To spy on an exported function in jest, you need to import all named exports and provide that object to the jest. spyOn function. That would look like this: import * as moduleApi from '@module/api'; // Somewhere in your test case or test suite jest.

How to spy on an exported function in Jest? ›

Yes, you can spy on a function in Jest using the jest. spyOn() method. This is useful when you want to ensure that a function is called with the correct arguments or a specific number of times.

How do I import into Jest? ›

The jest object is automatically in scope within every test file. The methods in the jest object help create mocks and let you control Jest's overall behavior. It can also be imported explicitly by via import {jest} from '@jest/globals' .

Top Articles
Latest Posts
Article information

Author: Patricia Veum II

Last Updated:

Views: 6002

Rating: 4.3 / 5 (64 voted)

Reviews: 95% of readers found this page helpful

Author information

Name: Patricia Veum II

Birthday: 1994-12-16

Address: 2064 Little Summit, Goldieton, MS 97651-0862

Phone: +6873952696715

Job: Principal Officer

Hobby: Rafting, Cabaret, Candle making, Jigsaw puzzles, Inline skating, Magic, Graffiti

Introduction: My name is Patricia Veum II, I am a vast, combative, smiling, famous, inexpensive, zealous, sparkling person who loves writing and wants to share my knowledge and understanding with you.