vitest-dev / vitest-dev/vitest
[Updated] vi.mock ignored when mocked module is pre-loaded transitively via setupFiles
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 17.1k
- Forks
- 2k
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 94
Description
Really sorry, updated the current one again. Thank god Claude could reproduce it without me making mistakes.
Describe the bug
I closed the previous topic (https://github.com/vitest-dev/vitest/issues/10089) because the description was misleading and there was no reproduction link.
vi.mock ignored when mocked module is pre-loaded transitively via setupFiles
Description
vi.mock is silently ignored when the module being mocked has already been loaded into the module cache by setupFiles — transitively through a barrel (index.ts).
vi.mock is hoisted and registered before the test runs, but the mock factory never intercepts the module because it was already executed and cached during the setupFiles phase.
Reproduction
Minimal repo: https://github.com/...
File structure:
src/
setup.ts
mocks/server.ts ← imports '../ui' (forces barrel execution)
ui/
consts.ts ← exports SORT
Simple.tsx ← imports @mui/material/Box
index.ts ← barrel: re-exports Simple + SORT
Simple.test.tsx ← vi.mock broken ❌
Simple.doMock.test.tsx ← workaround ✅
setup.ts:
import { beforeAll, afterEach, afterAll } from 'vitest';
import { server } from './mocks/server';
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
mocks/server.ts:
import { setupServer } from 'msw/node';
import '../ui'; // forces barrel → Simple.tsx → @mui/material/Box into cache
export const server = setupServer();
ui/index.ts:
export { Simple } from './Simple';
export { SORT } from './consts';
ui/Simple.tsx:
import Box from '@mui/material/Box';
export function Simple() {
return <Box><p>simple</p></Box>;
}
ui/Simple.test.tsx (broken):
import { render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { Simple } from './Simple';
vi.mock('@mui/material/Box', () => ({
default: ({ children }: React.PropsWithChildren) => (
<div data-testid='box'>{children}</div>
),
}));
describe('vi.mock (broken)', () => {
it('fails — real MuiBox renders instead of mock', () => {
render(<Simple />);
expect(screen.getByTestId('box')).toBeInTheDocument(); // ❌
});
});
Expected behavior
vi.mock('@mui/material/Box', ...) intercepts the module and the test passes.
Actual behavior
The real @mui/material/Box renders (class="MuiBox-root"). The mock is ignored because Box was already executed and cached during setupFiles before vi.mock could intercept it.
Workaround
// ui/Simple.doMock.test.tsx
vi.resetModules(); // clear the pre-loaded cache
vi.doMock('@mui/material/Box', () => ({
default: ({ children }: React.PropsWithChildren) => (
<div data-testid='box'>{children}</div>
),
}));
it('passes', async () => {
const { Simple } = await import('./Simple'); // fresh load gets the mock
render(<Simple />);
expect(screen.getByTestId('box')).toBeInTheDocument(); // ✅
});
Vitest version
4.1.2
Additional context
The bug only manifests when the module being mocked is loaded transitively through a barrel during setupFiles. Direct imports in setupFiles from the mocked module itself exhibit the same issue, but this case is particularly surprising because the test file imports Simple directly (not via the barrel) and there is no visible connection between setupFiles and the mock target.
Reproduction
https://github.com/Ghostblad3/vitest-bug
System Info
System:
OS: Linux 6.6 Ubuntu 24.04.3 LTS 24.04.3 LTS (Noble Numbat)
CPU: (14) x64 Intel(R) Core(TM) Ultra 7 265U
Memory: 12.64 GB / 15.35 GB
Container: Yes
Shell: 5.2.21 - /bin/bash
Binaries:
Node: 20.19.2 - /home/igris/.nvm/versions/node/v20.19.2/bin/node
Yarn: 1.22.22 - /home/igris/.nvm/versions/node/v20.19.2/bin/yarn
npm: 10.8.2 - /home/igris/.nvm/versions/node/v20.19.2/bin/npm
pnpm: 10.29.2 - /mnt/c/nvm4w/nodejs/pnpm
bun: 1.3.5 - /home/igris/.bun/bin/bun
npmPackages:
@vitejs/plugin-react: ~5.1.4 => 5.1.4
vite: ^8.0.3 => 8.0.3
vitest: ~4.1.2 => 4.1.2
Used Package Manager
pnpm
Validations
- Follow our Code of Conduct
- Read the Contributing Guidelines.
- Read the docs.
- Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- The provided reproduction is a minimal reproducible example of the bug.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked Ghostblad3/vitest-bug reproduction, especially setup.ts, mocks/server.ts, ui/index.ts, ui/Simple.tsx, and the broken and workaround test files. Run the reproduction and compare vi.mock with vi.resetModules plus vi.doMock. Done means the standard vi.mock test intercepts @mui/material/Box even when it was loaded transitively through setupFiles.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript, vite
- Domain
- testing-qa, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100