The Developer's Quarterly · Testing in JavaScriptVol. I · January 2026

jest.fn() vs jest.mock()Know the Difference

Both create fakes. But one replaces a function, the other replaces an entire module, and confusing the two leads to tests that do not work the way you think.

When writing unit tests, you almost always need to replace something real with something fake: a network call, a database query, or a callback. Jest gives you two tools for this. They look similar on the surface but operate at very different levels.

jest.fn() creates a single mock function you control inline. jest.mock() intercepts an entire module before your tests run. That difference is the whole story.

The two tools

jest.fn() function level

Creates a single mock function you control inline.

  • Records all calls
  • Configurable return values
  • Used as props or callbacks
  • Lives inside test body

jest.mock() module level

Intercepts an entire module before tests run.

  • Replaces all module exports
  • Hoisted above imports
  • Used for dependencies
  • Lives at top of file

jest.fn() in practice

You call jest.fn() inside a test to create a throwaway fake. It does nothing by default, it simply records every call made to it. You can then assert on those calls or configure it to return a value that drives the code path you want to test.

Example
const onSubmit = jest.fn(); render(<MyForm onSubmit={onSubmit} />); userEvent.click(screen.getByText('Submit')); expect(onSubmit).toHaveBeenCalledWith({ name: 'Alice' }); expect(onSubmit).toHaveBeenCalledTimes(1);

It is also easy to make the function return something useful. That is often all you need when the fake is passed in as a prop or an argument rather than imported from another file.

Common variations
const getUser = jest.fn().mockResolvedValue({ name: 'Alice' }); const fn = jest.fn().mockImplementation((x) => x * 2);

jest.mock() in practice

When the code you are testing imports a module internally, for example an API client or a logger, you cannot swap it out by passing a prop. That is where jest.mock() comes in. It intercepts the module at the import level before any test code runs.

Module mock
jest.mock('./emailService'); import { sendEmail } from './emailService'; import { registerUser } from './registerUser'; test('sends a welcome email on registration', async () => { await registerUser({ email: 'alice@example.com' }); expect(sendEmail).toHaveBeenCalledWith( 'alice@example.com', 'Welcome!' ); });

Here, registerUser internally calls sendEmail, but because Jest intercepted the module, the function it receives is the mock. No real email is ever sent.

You can also provide a factory when you need exact control over what the module exports.

Factory mock
jest.mock('./api', () => ({ fetchUser: jest.fn().mockResolvedValue({ name: 'Alice' }), deleteUser: jest.fn(), }));

The hoisting trap

Jest hoists jest.mock() to the top of the file automatically, even if you write it after your imports. That is what makes the interception work, but it also means you cannot freely reference local variables inside the mock factory.

Hoisting example
const myValue = 'hello'; jest.mock('./utils', () => ({ getValue: () => myValue, }));

That pattern fails because myValue is not available at the moment Jest evaluates the hoisted factory. One exception exists: variables whose names start with mock are allowed precisely to work around this limitation.

Side by side

Feature jest.fn() jest.mock()
Scope Single function Entire module
Where it lives Inside test body Top of file
Hoisted No Yes, automatically
Best for Callbacks, props, handlers Imports, dependencies
Auto-mocks exports No Yes, if no factory
Records calls Yes Yes, each export is a jest.fn()

The rule of thumb

If you are passing the fake as a prop or an argument, use jest.fn(). If the code you are testing imports something internally that you need to replace, use jest.mock().

They are not competitors. You will often use both in the same test file: jest.mock() intercepts the module at the top, and jest.fn() gives you fine-grained control over individual functions within it.