Skip to content

Unexpected behavior when in-source tests utilize vi.mock without globals #6829

Closed
@deen13

Description

@deen13

Description

In our project, we utilize in-source testing with Vitest and have disabled the globals option. This setup requires us to import vi at the top level to use mocks, while other test methods (e.g., it, expect) are imported from import.meta.vitest within each test block. However, importing vi at the top level introduces issues in production: it breaks the code by including test dependencies in the main application bundle, which should not reference any testing libraries outside of test contexts.

Ideally, vi should also be accessible directly from import.meta.vitest, allowing us to keep all testing-related imports consistent and limited to the test context. This would prevent production issues caused by top-level test imports while keeping the code cleaner and better scoped.

Error Stacktrace

Attempting to import vi from import.meta.vitest triggers the following error:

Error: There are some problems in resolving the mocks API.
You may encounter this issue when importing the mocks API from another module other than 'vitest'.
To fix this issue you can either:
- import the mocks API directly from 'vitest'
- enable the 'globals' options
    at /Users/deen/Code/vitest-vi-meta-import/foo-vi-meta-import.ts:2:101

While importing vi directly from vitest at the top level resolves this error for testing purposes, it causes production errors when serving the web app:

Uncaught Error: Vitest failed to access its internal state.

One of the following is possible:
- "vitest" is imported directly without running the "vitest" command
- "vitest" is imported inside "globalSetup" (to fix this, use "setupFiles" instead, because "globalSetup" runs in a different context)
- Otherwise, it might be a Vitest bug. Please report it to https://github.com/vitest-dev/vitest/issues

    at getWorkerState (vitest.js?v=27661102:11544:11)
    at getCurrentEnvironment (vitest.js?v=27661102:11549:17)
    at createExpect (vitest.js?v=27661102:14091:20)
    at vitest.js?v=27661102:14136:20

Expected Behavior

The ideal solution would be to access vi directly from import.meta.vitest, alongside it and expect, so that all testing imports are consistent and scoped within the test context. This would eliminate the need for top-level imports solely for mocking, preventing test dependencies from affecting production code. Here’s the desired structure:

// utils.ts
import { calculate } from "./utils.ts"

export function foo() {
  return calculate(1);
}

if (import.meta.vitest) {
  const { it, expect, vi } = import.meta.vitest; // vi available from import.meta.vitest

  vi.mock(import("./utils.ts"), () => {
    return {
      "calculate": (x: number) => x,
    };
  });

  it("should return one", () => {
    expect(foo()).toBe(1);
  });
}

Relevant Code in Vitest

After some investigation, it seems that this part of Vitest’s codebase might be related, as it appears to control how mocks are hoisted.

Reproduction

https://github.com/deen13/vitest-vi-meta-import

System Info

System:
  OS: macOS 14.6
  CPU: (8) arm64 Apple M3
  Memory: 56.94 MB / 24.00 GB
  Shell: 5.9 - /bin/zsh
Binaries:
  Node: 20.11.1 - ~/.nvm/versions/node/v20.11.1/bin/node
  Yarn: 1.22.22 - /opt/homebrew/bin/yarn
  npm: 10.2.4 - ~/.nvm/versions/node/v20.11.1/bin/npm
  pnpm: 9.12.1 - /opt/homebrew/bin/pnpm
Browsers:
  Chrome: 130.0.6723.71
  Safari: 17.6
npmPackages:
  vite: ^5.4.10 => 5.4.10 
  vitest: ^2.1.4 => 2.1.4

Used Package Manager

npm

Validations

Metadata

Metadata

Assignees

No one assigned

    Labels

    p3-minor-bugAn edge case that only affects very specific usage (priority)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions