Sometimes you need to replace an entire imported module, not just one function. This lesson covers jest.mock(), how it hoists to the top of the file, and how to provide a custom factory for the mock's implementation.
Replacing a Module with jest.mock()
jest.mock('./modulePath') tells Jest to replace every import of that module (within the current test file) with a mock version. Without a factory function, Jest auto-mocks the module, replacing every exported function with an empty jest.fn().
Jest hoists jest.mock() calls to the top of the file automatically (using Babel), so the mock is in place before any import/require statements run — this is why jest.mock() calls work even when written after your imports.
// emailClient.js
module.exports = {
send: (to, subject) => { /* calls a real email API */ },
};
// notifications.test.js
jest.mock('./emailClient');
const emailClient = require('./emailClient');
const { notifyUser } = require('./notifications');
test('calls emailClient.send with the right arguments', () => {
notifyUser({ email: 'ada@example.com' });
expect(emailClient.send).toHaveBeenCalledWith('ada@example.com', expect.any(String));
});
emailClient.send is automatically replaced with a jest.fn(), so no real email is sent during the test.
Sometimes you want to mock only part of a module while keeping the rest of its real behavior. jest.requireActual() inside a factory retrieves the real module so you can spread its exports and override just what you need.
jest.mock('./mathUtils', () => {
const actual = jest.requireActual('./mathUtils');
return {
...actual,
random: jest.fn().mockReturnValue(0.5), // only randomness is mocked
};
});
add, subtract, and every other real export from mathUtils keep working normally; only random is replaced.
Understanding jest.mock() Hoisting
Because Babel hoists jest.mock() calls above imports, referencing an outer-scope variable inside the factory (one not prefixed with mock) throws an "out-of-scope variable" error. Jest allows variable names starting with mock as an exception to this rule.
Naming mock-related variables with a mock prefix avoids Jest's out-of-scope variable restriction.
Common Mistakes
Referencing a non-mock-prefixed outer variable inside a jest.mock() factory and hitting a hoisting error.
Forgetting that auto-mocking replaces every exported function with an empty mock returning undefined.
Mocking an entire module when only a partial mock (via jest.requireActual()) was actually needed.
Not calling jest.resetModules() when a test needs a completely fresh module instance.
Key Takeaways
jest.mock('./path') replaces every import of a module within the test file.
A factory function as the second argument customizes exactly what the mock module exports.
jest.requireActual() inside a factory enables partial mocking of a module.
Jest hoists jest.mock() calls above imports; only mock-prefixed variables can be referenced inside factories.
Pro Tip
When a jest.mock() factory needs a shared mock function across multiple tests, declare it with a mock prefix (e.g. mockSend) at the top of the file — it satisfies Jest's hoisting rule and stays easy to reconfigure per test.
You now understand how to mock entire modules. Next, learn about manual mocks stored in a __mocks__ folder.