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.

Why is this an issue?

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.

Code examples

Noncompliant code example

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();
});

Compliant solution

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();
});

Noncompliant code example

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 () => {
    // ...
  });
});

Compliant solution

import { beforeEach, describe, it, vi } from "vitest";

vi.mock("fs");
vi.mock("path");

describe("LCPServer", () => {
  beforeEach(() => {
    vi.restoreAllMocks();
  });

  it("loads dictionaries", async () => {
    // ...
  });
});

Noncompliant code example

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();
});

Compliant solution

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();
});

Resources

Documentation