Migrating from Jest to Vitest: Resolving Mock Hoisting Differences
0 reputation · 18 Nov 2024, 05:40 UTC
0 reputation · 18 Nov 2024, 05:40 UTC
Gradually shift a small application’s test suite from Jest to Vitest while keeping local development and CI pipelines operational.
Vitest runs in a separate Node environment and hoists vi.mock calls statically above all imports, unlike Jest’s transform‑based timing. Path aliases and browser globals must be re‑declared, and in‑memory fixtures used by the legacy runner are not shared with Vitest’s isolated workers.
When migrating a test that relies on import order, will Vitest’s hoisting break the original execution flow? Is there a configuration knob to align mock timing with Jest, or must the test be refactored? Additionally, can we maintain shared fixtures across both runners without duplicating state?
vi.mock hoisting to match Jest’s behavior?Vitest does not provide a configuration knob to defer vi.mock hoisting to match Jest’s execution timing. Vitest statically hoists vi.mock calls to the top of the file to ensure dependencies are mocked before any code executes. To replicate Jest's specific import order, you must refactor the test to use vi.doMock combined with dynamic imports.
Shared in-memory fixtures cannot be accessed between Jest and Vitest during parallel execution because they run in isolated Node.js processes or workers. To maintain shared state, you must move fixtures to an external store, such as a database, Redis, or a file-based cache.
In Jest, the transform-based approach sometimes allows for more flexibility in when mocks are evaluated. Vitest uses Vite's ESM-based transformation which prioritizes hoisting. If a migrated test imports a module that itself imports a mocked dependency, Vitest will ensure the mock is registered before that module is evaluated, regardless of where the vi.mock call appears in your source code.
vi.doMock: For cases where hoisting breaks execution, replace vi.mock with vi.doMock.vi.doMock, you must perform the import() inside the test function or a hook to ensure the mock is active before the module is loaded.// Example of non-hoisted mock in Vitest
test('uses manual mock without hoisting', async () => {
vi.doMock('./module', () => ({ data: vi.fn(() => 'mocked') }));
const { data } = await import('./module');
expect(data()).toBe('mocked');
});
Since Jest and Vitest do not share memory, in-memory fixtures are effectively isolated. To avoid duplicating state or inconsistency:
Diagnostic Question: Does your application rely on specific side effects triggered during the module evaluation, or are the mocks simply providing return values?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.