Lesson 11 / 25
Module Mocking and Hoisting
jest.mock and vi.mock, and why they run first.
Replace an import for the whole test file
jest.mock('./path') / vi.mock('./path') replace a module everywhere it is imported by the file under test. With no factory, each export becomes an automatic mock; with a factory you return the replacement exports. These calls are hoisted to the top of the file, above your imports, so the mock is in place before the module loads. A consequence: a factory cannot use ordinary variables declared in the test file. In Vitest, use vi.hoisted to create values the factory may reference; in Jest, variables whose names start with mock are allowed in the factory. To keep most of the real module, spread the original (vi.importActual / jest.requireActual) and override one export. Mock your own boundary modules (an API client, analytics) rather than third-party internals.
Mocking an analytics module
Vitest with vi.hoisted; the Jest equivalent is shown below.
import { beforeEach, expect, it, vi } from 'vitest';
import { checkout } from './checkout';
const { track } = vi.hoisted(() => ({ track: vi.fn() }));
vi.mock('./analytics', () => ({ track })); // hoisted above the imports
vi.mock('./config', async (importOriginal) => {
const actual = await importOriginal<typeof import('./config')>();
return { ...actual, featureFlags: { newCheckout: true } }; // override one export
});
beforeEach(() => track.mockClear());
it('tracks a completed checkout', async () => {
await checkout({ cartId: 'c1' });
expect(track).toHaveBeenCalledWith('checkout_completed', { cartId: 'c1' });
});
// Jest equivalent:
// const mockTrack = jest.fn();
// jest.mock('./analytics', () => ({ track: (...args: unknown[]) => mockTrack(...args) }));A stunt double
A module mock is a stunt double: it stands in for the real actor in risky scenes. Useful, but the audience must not mistake the double's performance for the star's; you still need some tests with the real module.
Quick check: Why can a vi.mock factory not use a normal const declared above it?
- Constants are frozen by Vitest
- Factories may only return strings
- Mocks run in a separate browser
- vi.mock is hoisted above the declarations, so the variable is not initialised yet
Answer
vi.mock is hoisted above the declarations, so the variable is not initialised yet — Use vi.hoisted (or the mock prefix in Jest).