Vitestで動的import系プラグインローダーをプロジェクト外の.tsに対して実行するとテストランナー自身のローダーと競合し得る
TypeScript
テスト
Vitest
知識
運用
ユーザーが用意した.tsファイルを実行時に動的ロードするプラグイン機構(任意パスのTypeScriptファイルをその場でトランスパイル・importするランタイムAPI)を実装している場合、その動的ロード処理自体をVitestのテスト内で、プロジェクトルート外(例: OSの一時ディレクトリ配下)に置いた対象ファイルに対して実行すると、正しく動作しないことがある。
症状
動的ロードしたモジュールのdefaultエクスポートが関数のはずが、実際には型が'object'になる(実質的に二重ラップされたような形になる)。同じコード・同じファイルを、Vitestを介さずプレーンな実行(トランスパイラCLIを直接使う実行など)で試すと正しく関数として取得できる。
原因
Vitestは内部でVite自身のモジュールグラフ・ESMローダーを使ってテストコードをトランスフォーム・実行している。プラグインローダー側が独自にESM loaderフック(loader registration)を使って動的importを行う設計だと、対象ファイルがVite自身の管理下(プロジェクトルート内)にない場合に、2つのローダーの解決経路が競合し、モジュールの形が想定と変わってしまう。プロジェクト内のファイルではこの競合が起きないか、Vite側の解決が優先されて症状が出ないことがあるため、「テスト対象と同じ階層に置いたテストは通るのに、意図的にルート外に置いた一時ファイルだけ壊れる」という現れ方をする。
判断基準
- このような動的import系の実行時プラグインローダーの統合テストは、Vitest(や類似のVite/webpackベースのテストランナー)内で本物の動的トランスパイルを実行して検証しない。ランナー自身のモジュール解決と競合する可能性があるため、テスト結果が実運用と乖離しうる。
- 代わりに、(1) ローダーモジュール自体をモックしてロジック(呼び出し引数の組み立て、返り値の後処理、エラーハンドリング)だけをテストランナー内で検証する、(2) 実際の動的トランスパイルが正しく動くことは、テストランナーを介さないプレーンな実行スクリプトで別途スモーク確認する、の2段に分離する。
検証
同一の動的ロード呼び出しを、対象テストランナー内と、テストランナーを介さない素の実行環境の両方で実行し、返ってくるモジュールの形(exportのキーと型)を比較する。差異があれば、テストランナー環境固有の競合を疑う。