This rule raises an issue when a Vitest vi.mock() call is not declared at module scope. Vitest hoists every vi.mock()
call to the top of the file before imports, so nesting it inside a test, hook, conditional, or helper does not provide per-test or runtime
mocking.
Vitest replaces modules with vi.mock() by rewriting the test file: each vi.mock() call is moved to the top of the file
and executed before static imports. The call always registers a file-wide mock, regardless of whether it appears inside it,
test, beforeEach, beforeAll, an if, or a helper function.
That hoisting makes nested placement misleading. A vi.mock() inside one test looks like a test-local stub, but Vitest still applies it
for the whole file. Later tests can inherit an unexpected mock, and developers can spend time debugging mock state that was never scoped the way the
source suggests.
Vitest already reports this at runtime with a console warning, and will throw an error starting with Vitest 5. Catching it statically means developers see the problem before ever running the tests.
Move vi.mock() to module scope when the mock should apply for the file. When you need a runtime or test-specific module replacement,
use vi.doMock() instead — it is not hoisted and only affects subsequent dynamic import() calls.
import { it, vi } from "vitest";
import { run } from "./runner.js";
it("uses the mocked API", async () => {
vi.mock("./api.js", () => ({ // Noncompliant: "vi.mock" is nested inside a test
load: vi.fn(),
}));
await run();
});
import { it, vi } from "vitest";
import { run } from "./runner.js";
vi.mock("./api.js", () => ({
load: vi.fn(),
}));
it("uses the mocked API", async () => {
await run();
});
import { beforeEach, describe, it, vi } from "vitest";
describe("LCPServer", () => {
beforeEach(() => {
vi.mock("fs"); // Noncompliant: "vi.mock" is nested inside a hook
vi.mock("path"); // Noncompliant: "vi.mock" is nested inside a hook
});
it("loads dictionaries", async () => {
// ...
});
});
import { beforeEach, describe, it, vi } from "vitest";
vi.mock("fs");
vi.mock("path");
describe("LCPServer", () => {
beforeEach(() => {
vi.restoreAllMocks();
});
it("loads dictionaries", async () => {
// ...
});
});
import { it, vi } from "vitest";
it("selects a test-specific implementation", async () => {
vi.mock("./api.js", () => ({ // Noncompliant: use "vi.doMock" for runtime mocking
load: vi.fn(),
}));
const { run } = await import("./runner.js");
await run();
});
import { it, vi } from "vitest";
it("selects a test-specific implementation", async () => {
vi.doMock("./api.js", () => ({
load: vi.fn(),
}));
const { run } = await import("./runner.js");
await run();
});
vi.mockvi.doMockimport()