5 min read

How To Mock The Return Value Of An Imported Function In Jest

Mocking is one of the most essential skills to creating proper tests using Jest. Without mocks, tests would always have to run all the actual code. In certain scenarios, this just isn’t feasible, relevant, or even necessary. That’s why it’s a common practice to mock certain imported codes and replace them with dummy code in tests.

In Jest and Vitest, this can be done quite easily through the use of different utility functions in different scenarios. For mocking imported modules, there is jest.mock or vi.mock. For mocking imported functions, there are mock functions and spy functions.

Examples of using these are as follows:

import * as moduleApi from '@module/api';

// Using only jest.fn
moduleApi.functionToMock = jest.fn();

// Using jest.mock and jest.fn
jest.mock("@module/api", () => ({
functionToMock: jest.fn()
}));

// Using jest.spyOn
jest.spyOn(moduleApi, 'functionToMock');
import * as moduleApi from '@module/api';
import { vi } from 'vitest';

// Using only vi.fn
moduleApi.functionToMock = vi.fn();

// Using vi.mock and vi.fn
vi.mock("@module/api", () => ({
    functionToMock: vi.fn()
}));

// Using vi.spyOn
vi.spyOn(moduleApi, 'functionToMock');

But in certain scenarios, you also want to mock the return value of the function that you’re importing into your code. Maybe your tests aim to specifically verify certain error flows. Maybe you just don’t care about executing the underlying code and want to skip it altogether by mocking and specifying a return value. Or maybe it’s not possible to run the function code in the test environment.

No matter the reason, this article will go over how to mock the return value of an imported function.

To mock the return value of an imported function, you have to either call mockReturnValue or mockImplementation on a mock function and then specify the return value. Which mock function you should use depends on your situation. Mainly, it’s about whether you want the original function code to be executed or not.

If you only want to mock the return value but want to keep the original function code untouched, then you should call the mockReturnValue in combination with a spy:

jest
  .spyOn(moduleApi, "functionToMock")
  .mockReturnValue({ someObjectProperty: 42 });
vi.spyOn(moduleApi, "functionToMock").mockReturnValue({
  someObjectProperty: 42,
});

If on top of the return value you also want to mock the function code, then you should call mockReturnValue in combination with mock function or call mockImplementation with an empty return callback when using spy:

// mockReturnValue + jest.fn()
moduleApi.functionToMock = jest.fn().mockReturnValue({ someObjectProperty: 42 });

// mockImplementation + jest.spyOn
jest.spyOn(moduleApi, 'functionToMock').mockImplementation(() => ({ someObjectProperty: 42 }));
// mockReturnValue + vi.fn()
moduleApi.functionToMock = vi.fn().mockReturnValue({ someObjectProperty: 42 });

// mockImplementation + vi.spyOn
vi.spyOn(moduleApi, 'functionToMock').mockImplementation(() => ({ someObjectProperty: 42 }));

If in either case you only want the return value to be mocked once or have a more fine-grained control over the order in which return values are mocked, then you should use the Once version of these functions instead. To control the order, you can call them multiple times on the same mock function. That would look as follows:

// mockReturnValue + jest.fn()
moduleApi.functionToMock = jest
	.fn()
	.mockReturnValueOnce({ someObjectProperty: 42 })
	.mockReturnValueOnce({ someObjectProperty: 20 });

// mockImplementation + jest.spyOn
jest.spyOn(moduleApi, 'functionToMock')
.mockImplementationOnce(() => ({ someObjectProperty: 42 }))
.mockImplementationOnce(() => ({ someObjectProperty: 20 }));
// mockReturnValue + vi.fn()
moduleApi.functionToMock = vi
	.fn()
	.mockReturnValueOnce({ someObjectProperty: 42 })
	.mockReturnValueOnce({ someObjectProperty: 20 });

// mockImplementation + vi.spyOn
vi.spyOn(moduleApi, 'functionToMock')
	.mockImplementationOnce(() => ({ someObjectProperty: 42 }))
	.mockImplementationOnce(() => ({ someObjectProperty: 20 }));

And that’s it, now you know how to mock the return value of an imported function, while optionally preserving the original implementation, limiting the number of mocks to a certain number, or configuring them in a certain order!