正解は「vi.mock の呼び出しがファイル先頭に巻き上げ(hoisting)されるため、mockValue がまだ初期化されていない状態で参照される」です。Vitest は内部的に Babel/SWC で vi.mock の呼び出しをファイルの一番上(import より前)に移動します。そのため、ファクトリ関数の定義時点ではトップレベルの const mockValue はまだ評価されておらず、参照すると ReferenceError になるか、意図しない値になります。他の選択肢については、vi.mock は同期的に処理されるため await は不要で、import より後に書いても巻き上げによって正しく適用されます。またファクトリ内で外部変数が使えないわけではなく、「巻き上げより前に定義された変数」なら参照可能です。vi.mock が import より先に動く理由ES Modules では import は静的に解決され、モジュールの評価順序がランタイムで変えられません。そのため Vitest は「モックをセットアップしてから対象モジュールを評価する」を実現するために、コード変換の段階で vi.mock を import より上に移動させています。これをホイスティングと呼びます。Jest の jest.mock と同じ仕組みで、Vitest の公式ドキュメントにも明記されています。外部変数を安全に使うには vi.hoistedモックファクトリから外部の値を参照したい場合、Vitest は vi.hoisted という API を提供しています。この中で定義した値は vi.mock と一緒に巻き上げられるので、参照順序の問題を解決できます。const { mockValue } = vi.hoisted(() => { return { mockValue: 'mocked!' } }) vi.mock('./utils', () => ({ greet: () => mockValue, }))実務でハマりやすいポイントトップレベルの変数をファクトリで直接参照しない: 巻き上げで ReferenceError になりますモック関数を後から差し替えたい時も vi.hoisted が有効: vi.fn() を hoisted 内で作っておけば、テスト側から mockReturnValue で制御できます型安全にしたい場合は importActual と組み合わせる: 元の実装の一部だけモックしたいケースで頻出です「vitest vi.mock 巻き上げ」や「vi.mock referenceerror」で検索するとこの落とし穴にハマった事例が大量に出てきます。仕組みを理解しておくと、エラーメッセージを見た瞬間に原因がわかるようになります。