Jest環境でAxios使用時に発生する Cannot use import statement outside a module
エラー解決方法
問題の説明
Vue.jsアプリケーションでAxiosを使用するテストを実行中に、次のエラーが発生するケースがあります:
SyntaxError: Cannot use import statement outside a module
この問題は通常、以下の状況で発生します:
- Axios 1.x以降を使用している
- Jestでテストを実行している
- テストファイル内で
import axios from "axios"
を使用している - GitHub ActionsなどのCI環境で発生することが多い
問題の背景
Axios 1.0.0以降、パッケージはECMAScript Modules(ESM) 形式でリリースされるようになりました。
JestはNode.js環境で動作するため、ESMではなくCommonJS形式を期待します。
発生原因と解決方針
このエラーの根本原因は:
- AxiosがデフォルトでESMモジュールを提供するようになった
- Jestがnode_modules内のコードをデフォルトで変換しない
- ESM形式のコードをCommonJS環境で実行しようとしている
解決には主に2つのアプローチがあります:
- JestにAxiosの変換を許可(推奨)
transformIgnorePatterns
で除外パターンを指定
- CommonJSビルドを明示的に使用
moduleNameMapper
でCJSバージョンを指定
解決方法
方法1: transformIgnorePatternsを使用(推奨)
最も確実な解決策は、JestにAxiosを変換対象に含めることです。
設定例(jest.config.js):
module.exports = {
transformIgnorePatterns: [
"node_modules/(?!axios)"
]
}
設定例(package.json):
{
"jest": {
"transformIgnorePatterns": ["node_modules/(?!axios)"]
}
}
設定例(Reactプロジェクトのpackage.json scripts):
{
"scripts": {
"test": "react-scripts test --transformIgnorePatterns \"node_modules/(?!axios)/\""
}
}
設定の解説
node_modules/(?!axios)
: axios以外のnode_modulesを無視?!(axios)
: "axios"を除外する(否定先読み)- この設定でJestはAxiosモジュールをトランスパイル対象に含める
方法2: moduleNameMapperでCommonJSビルドを指定
Axiosが提供するCommonJSビルドを明示的に使用する方法:
module.exports = {
moduleNameMapper: {
"axios": "axios/dist/node/axios.cjs"
}
}
またはpackage.jsonの場合:
{
"jest": {
"moduleNameMapper": {
"^axios$": "axios/dist/node/axios.cjs"
}
}
}
注意点
この方法はaxios-mock-adapter
などの関連ライブラリを使用している場合に互換性問題が発生する可能性があります。問題が発生する場合は方法1をお試しください。
その他の注意点と補足
Jestのキャッシュクリア
設定変更後にエラーが継続する場合は、キャッシュをクリア:
npm test -- --clearCache
TypeScript環境での設定例
ts-jestを使用する場合の設定例:
import type { JestConfigWithTsJest } from 'ts-jest';
const config: JestConfigWithTsJest = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
moduleNameMapper: {
'^axios$': 'axios/dist/node/axios.cjs'
}
};
export default config;
複数ライブラリを対象にする場合
複数のESMライブラリを使用する場合の設定例:
const esModules = ['axios', 'another-library'].join('|');
module.exports = {
transformIgnorePatterns: [`node_modules/(?!(${esModules}))`]
};
動作原理について(技術的背景)
- Jestのデフォルト動作:
node_modules
内のモジュールは無視 - transformIgnorePatterns: 無視リストから特定モジュールを除外
- Axios互換モード切替: CJSビルドには
axios.cjs
形式が提供される - Babel連携: ESM→CJS変換はBabelが自動処理
推奨ソリューションの比較
方法 | メリット | デメリット | 推奨度 |
---|---|---|---|
transformIgnorePatterns | 他のライブラリにも適用可能 | 設定が複雑な場合あり | ★★★★★ |
moduleNameMapper | 特定ビルドを直接指定 | 互換性問題の可能性 | ★★★☆☆ |
テストスクリプト変更 | 即時対応可能 | プロジェクト全体に適用されない | ★★☆☆☆ |
ベストプラクティスの推奨
- まず
transformIgnorePatterns
のアプローチを試す - 互換性問題が発生した場合は
moduleNameMapper
の使用を検討 - キャッシュ問題が発生したら
npm test -- --clearCache
を実行
回避策と代替手段
Axiosバージョンを固定(緊急回避用)
一時的な回避策としてpackage.jsonで0.x系を指定:
{
"axios": "0.27.2"
}
fetch APIへの置き換え
小規模プロジェクトならブラウザ標準APIへの置き換えも選択肢:
try {
const response = await fetch("api/data", { method: "GET" });
const data = await response.json();
return data;
} catch (error) {
handleError(error);
}
注意点
- Axios固有の機能(インターセプタ等)が使用不可
- 機能縮小となるため、あくまで一時的解決策
結論
JestとAxiosのインポート問題は最新のモジュールシステムの差異が主原因です。以下の手順で解決できます:
- Jest設定ファイル(
jest.config.js
)を開く transformIgnorePatterns
に"node_modules/(?!axios)"
を追加- テスト実行
npm test
- 問題が解決しない場合はキャッシュクリア
npm test -- --clearCache
これらの設定はAxiosに限らず、JestでESMモジュールを使用する際の基本パターンとして応用できます。