Jest の “Cannot use import statement outside a module” を根治する手順
Jest でテストを実行しようとした際に、「Cannot use import statement outside a module」というエラーに遭遇することがあります。このエラーは ES Modules(ESM)形式のコードを Jest が正しく解釈できていないために発生するものです。本記事では、このエラーの根本原因から実践的な解決策まで、段階的にわかりやすく解説していきます。
背景
JavaScript のモジュールシステムには、大きく分けて CommonJS と ES Modules(ESM)の 2 つの方式が存在します。
mermaidflowchart TB
js["JavaScript<br/>モジュールシステム"]
cjs["CommonJS<br/>(require/module.exports)"]
esm["ES Modules<br/>(import/export)"]
node["Node.js<br/>環境"]
browser["ブラウザ<br/>環境"]
js --> cjs
js --> esm
cjs -->|主に使用| node
esm -->|主に使用| browser
esm -->|サポート拡大| node
図の要点: JavaScript には CommonJS と ES Modules という 2 つのモジュール方式があり、それぞれ異なる環境で主に使われてきました。
CommonJS と ES Modules の違い
| # | 項目 | CommonJS | ES Modules |
|---|---|---|---|
| 1 | 読み込み構文 | require() | import |
| 2 | 出力構文 | module.exports | export |
| 3 | 読み込みタイミング | 実行時(動的) | パース時(静的) |
| 4 | Node.js サポート | デフォルト | 明示的設定が必要 |
| 5 | ブラウザサポート | ✕(バンドラー必須) | ○(ネイティブサポート) |
Node.js は長らく CommonJS を標準としてきましたが、近年では ES Modules のサポートも進んでいます。Jest は Node.js 環境で動作するテストフレームワークであるため、デフォルトでは CommonJS 形式を期待しているのです。
Jest のデフォルト動作
Jest は以下のような動作フローでテストファイルを処理します。
mermaidflowchart LR
test["テストファイル"]
jest["Jest"]
trans["変換処理<br/>(babel-jest)"]
exec["実行環境<br/>(Node.js)"]
result["テスト結果"]
test -->|読み込み| jest
jest -->|変換| trans
trans -->|CommonJS化| exec
exec -->|実行| result
この変換処理が正しく設定されていない場合、ES Modules 構文がそのまま Node.js に渡されてしまい、エラーが発生してしまいます。
課題
「Cannot use import statement outside a module」エラーが発生する主な原因を整理しましょう。
エラーの発生パターン
このエラーは以下のような状況で発生します。
エラーコード: SyntaxError: Cannot use import statement outside a module
エラーメッセージ例:
swiftSyntaxError: Cannot use import statement outside a module
at Object.compileFunction (node:vm:352:18)
at wrapSafe (node:internal/modules/cjs/loader:1033:15)
at Module._compile (node:internal/modules/cjs/loader:1069:27)
発生条件:
- ES Modules 構文(
import/export)を使用したファイルをテストしようとしている - Jest の設定で ES Modules を適切に処理する設定がされていない
package.jsonに"type": "module"が設定されている、または.mjs拡張子を使用している- 外部ライブラリが ES Modules のみで提供されている
根本的な問題
以下の図は、エラーが発生するメカニズムを示しています。
mermaidflowchart TD
code["ESM 構文の<br/>コード"]
jest["Jest"]
check{{"変換設定<br/>あり?"}}
transform["Babel/SWC<br/>による変換"]
cjs["CommonJS<br/>形式"]
direct["ESM 構文<br/>そのまま"]
node["Node.js<br/>実行環境"]
success["✓ テスト成功"]
error["✕ エラー発生"]
code --> jest
jest --> check
check -->|Yes| transform
check -->|No| direct
transform --> cjs
cjs --> node
direct --> node
node --> success
node --> error
図で理解できる要点:
- Jest が ES Modules を CommonJS に変換する設定がない場合、エラーが発生する
- 適切な変換処理を設定することで、ES Modules のコードも Jest で実行できる
- 変換ツールには Babel や SWC などの選択肢がある
解決策
このエラーを根治するには、複数のアプローチがあります。プロジェクトの状況に応じて最適な方法を選択しましょう。
解決方法の選択フロー
mermaidflowchart TD
start["エラー発生"]
q1{{"TypeScript<br/>使用?"}}
q2{{"Next.js など<br/>フレームワーク?"}}
q3{{"最新の<br/>環境?"}}
sol1["① ts-jest を使用"]
sol2["② フレームワークの<br/>Jest 設定を使用"]
sol3["③ babel-jest で<br/>変換設定"]
sol4["④ Experimental<br/>ESM サポート"]
start --> q1
q1 -->|Yes| sol1
q1 -->|No| q2
q2 -->|Yes| sol2
q2 -->|No| q3
q3 -->|Yes| sol4
q3 -->|No| sol3
それぞれの解決方法を詳しく見ていきましょう。
① TypeScript プロジェクトの場合: ts-jest を使用
TypeScript を使用しているプロジェクトでは、ts-jest が最も推奨される解決策です。
必要なパッケージのインストール:
bashyarn add -D jest ts-jest @types/jest
Jest 設定の初期化:
bashyarn ts-jest config:init
このコマンドにより、jest.config.js ファイルが自動生成されます。
生成される jest.config.js の内容:
javascript/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
// ts-jest をプリセットとして使用
preset: 'ts-jest',
// Node.js 環境でテストを実行
testEnvironment: 'node',
};
この設定により、Jest は TypeScript ファイルを自動的に変換して実行できるようになります。
より詳細な設定が必要な場合:
javascript/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
// TypeScript の設定を明示的に指定
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: {
// ESM を有効化
esModuleInterop: true,
allowSyntheticDefaultImports: true,
},
},
],
},
};
この設定では、transform オプションで TypeScript ファイル(.ts、.tsx)の変換方法を詳細に指定しています。
tsconfig.json の設定確認:
json{
"compilerOptions": {
"module": "ESNext",
"target": "ES2020",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "node"
}
}
これらの設定により、TypeScript プロジェクトで ES Modules を使用しながら、Jest でのテストも問題なく実行できるようになります。
② Babel を使用した変換設定
TypeScript を使用していない JavaScript プロジェクトや、既に Babel を使用しているプロジェクトでは、babel-jest を活用します。
必要なパッケージのインストール:
bashyarn add -D jest babel-jest @babel/core @babel/preset-env
Babel 設定ファイルの作成(babel.config.js):
javascriptmodule.exports = {
presets: [
// Node.js の現在のバージョンに合わせて変換
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
};
この設定により、ES Modules 構文が CommonJS に自動変換されます。
React を使用している場合の Babel 設定:
javascriptmodule.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
// React の JSX 構文にも対応
'@babel/preset-react',
],
};
Jest 設定ファイル(jest.config.js):
javascriptmodule.exports = {
// テスト環境の指定
testEnvironment: 'node',
// Babel による変換を有効化
transform: {
'^.+\\.(js|jsx)$': 'babel-jest',
},
// 変換対象外のモジュールを指定
transformIgnorePatterns: [
'node_modules/(?!(module-to-transform)/)',
],
};
transformIgnorePatterns は、通常 node_modules 内のファイルは変換されませんが、ES Modules のみで提供されているパッケージがある場合に使用します。
③ 外部パッケージが ESM のみの場合の対処
最近の npm パッケージの中には、ES Modules 形式でのみ提供されているものがあります。この場合、特別な設定が必要です。
問題となるパッケージの例:
node-fetchv3 以降chalkv5 以降gotv12 以降
Jest 設定での対処方法:
javascriptmodule.exports = {
testEnvironment: 'node',
// ESM パッケージを変換対象に含める
transformIgnorePatterns: [
'node_modules/(?!(node-fetch|chalk|got)/)',
],
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
},
};
この設定により、指定したパッケージも Babel で変換されるようになります。
複数のパッケージを指定する場合:
javascriptconst esModules = [
'node-fetch',
'chalk',
'got',
'p-limit',
'yocto-queue',
].join('|');
module.exports = {
transformIgnorePatterns: [
`node_modules/(?!(${esModules})/)`,
],
};
配列で管理することで、後からパッケージを追加しやすくなります。
④ Jest の Experimental ESM サポートを使用
Jest 28 以降では、実験的に ES Modules のネイティブサポートが提供されています。ただし、これは実験的機能であることに注意が必要です。
package.json の設定:
json{
"type": "module"
}
jest.config.js の作成(拡張子を .mjs に変更):
javascript// jest.config.mjs として保存
export default {
testEnvironment: 'node',
// 拡張子の明示的な指定
extensionsToTreatAsEsm: ['.ts', '.tsx'],
// モジュール名のマッピング
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
// TypeScript 使用時の設定
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
useESM: true,
},
],
},
};
テスト実行コマンド:
bashnode --experimental-vm-modules node_modules/jest/bin/jest.js
package.json のスクリプト設定:
json{
"scripts": {
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
}
}
この方法は最も新しいアプローチですが、実験的機能であるため、本番プロジェクトでの使用は慎重に検討してください。
具体例
実際のプロジェクトでエラーが発生し、解決するまでの流れを見ていきましょう。
エラーが発生するコード例
テスト対象のコード(src/utils/calculator.js):
javascript// ES Modules 構文を使用
export const add = (a, b) => {
return a + b;
};
javascriptexport const subtract = (a, b) => {
return a - b;
};
javascriptexport const multiply = (a, b) => {
return a * b;
};
各関数をモジュールとして export しています。
テストコード(src/utils/calculator.test.js):
javascript// ES Modules 形式でインポート
import { add, subtract, multiply } from './calculator.js';
javascript// Jest のテストケース
describe('Calculator functions', () => {
test('add function should return sum of two numbers', () => {
expect(add(2, 3)).toBe(5);
});
});
javascriptdescribe('Calculator functions', () => {
test('subtract function should return difference', () => {
expect(subtract(5, 3)).toBe(2);
});
test('multiply function should return product', () => {
expect(multiply(3, 4)).toBe(12);
});
});
エラーの発生
テスト実行:
bashyarn jest
エラー出力:
javascriptFAIL src/utils/calculator.test.js
● Test suite failed to run
Jest encountered an unexpected token
SyntaxError: Cannot use import statement outside a module
1 | import { add, subtract, multiply } from './calculator.js';
| ^^^^^^
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)
このエラーメッセージから、Jest が import 文を理解できていないことがわかります。
解決手順(Babel を使用する場合)
手順 1: 必要なパッケージのインストール:
bashyarn add -D babel-jest @babel/core @babel/preset-env
手順 2: Babel 設定ファイルの作成:
babel.config.js を作成します。
javascriptmodule.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
};
この設定により、現在の Node.js バージョンに合わせてコードが変換されます。
手順 3: Jest 設定ファイルの作成:
jest.config.js を作成します。
javascriptmodule.exports = {
testEnvironment: 'node',
// Babel による変換を有効化
transform: {
'^.+\\.js$': 'babel-jest',
},
};
手順 4: テストの再実行:
bashyarn jest
成功時の出力:
bashPASS src/utils/calculator.test.js
Calculator functions
✓ add function should return sum of two numbers (3 ms)
✓ subtract function should return difference (1 ms)
✓ multiply function should return product (1 ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
これで、ES Modules 構文を使用したコードが Jest で正しくテストできるようになりました。
TypeScript プロジェクトでの具体例
テスト対象のコード(src/services/userService.ts):
typescript// TypeScript の型定義
export interface User {
id: number;
name: string;
email: string;
}
typescript// ユーザーを作成する関数
export const createUser = (
name: string,
email: string
): User => {
return {
id: Math.floor(Math.random() * 1000),
name,
email,
};
};
typescript// ユーザーの妥当性を検証する関数
export const validateUser = (user: User): boolean => {
return user.name.length > 0 && user.email.includes('@');
};
テストコード(src/services/userService.test.ts):
typescriptimport {
createUser,
validateUser,
User,
} from './userService';
typescriptdescribe('User Service', () => {
test('createUser should create a valid user object', () => {
const user = createUser('太郎', 'taro@example.com');
expect(user).toHaveProperty('id');
expect(user.name).toBe('太郎');
expect(user.email).toBe('taro@example.com');
});
});
typescriptdescribe('User Service', () => {
test('validateUser should return true for valid user', () => {
const validUser: User = {
id: 1,
name: '花子',
email: 'hanako@example.com',
};
expect(validateUser(validUser)).toBe(true);
});
test('validateUser should return false for invalid user', () => {
const invalidUser: User = {
id: 2,
name: '',
email: 'invalid-email',
};
expect(validateUser(invalidUser)).toBe(false);
});
});
解決手順(ts-jest を使用):
手順 1: パッケージのインストール:
bashyarn add -D jest ts-jest @types/jest typescript
手順 2: Jest 設定の初期化:
bashyarn ts-jest config:init
手順 3: 生成された jest.config.js の確認:
javascript/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
手順 4: tsconfig.json の設定確認:
json{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"esModuleInterop": true,
"moduleResolution": "node",
"strict": true
},
"include": ["src/**/*"]
}
手順 5: テストの実行:
bashyarn jest
成功時の出力:
sqlPASS src/services/userService.test.ts
User Service
✓ createUser should create a valid user object (4 ms)
✓ validateUser should return true for valid user (1 ms)
✓ validateUser should return false for invalid user (1 ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
TypeScript プロジェクトでも、適切な設定により ES Modules をスムーズに使用できます。
トラブルシューティング: よくある追加のエラー
エラー 1: Cannot find module エラーが発生する場合
javascriptError: Cannot find module './calculator.js' from 'calculator.test.js'
解決方法: moduleNameMapper を設定します。
javascriptmodule.exports = {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};
エラー 2: regeneratorRuntime is not defined エラーが発生する場合
vbnetReferenceError: regeneratorRuntime is not defined
解決方法: @babel/plugin-transform-runtime を追加します。
bashyarn add -D @babel/plugin-transform-runtime
javascript// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: { node: 'current' },
},
],
],
plugins: ['@babel/plugin-transform-runtime'],
};
これらの設定により、async/await を使用したコードも正しく動作します。
まとめ
「Cannot use import statement outside a module」エラーは、Jest が ES Modules 構文を適切に処理できていないことが原因で発生します。しかし、本記事で紹介した設定を行うことで、このエラーを根本から解決できるのです。
解決方法は主に以下の 4 つがあります。
| # | 方法 | 対象プロジェクト | 推奨度 |
|---|---|---|---|
| 1 | ts-jest を使用 | TypeScript プロジェクト | ★★★ |
| 2 | babel-jest を使用 | JavaScript プロジェクト | ★★★ |
| 3 | transformIgnorePatterns の設定 | ESM パッケージを使用 | ★★☆ |
| 4 | Experimental ESM サポート | 最新環境・実験的 | ★☆☆ |
重要なポイント:
- プロジェクトの技術スタックに応じて適切な解決方法を選択しましょう
- TypeScript を使用している場合は
ts-jestが最も確実で推奨されます - 外部パッケージが ESM のみの場合は
transformIgnorePatternsの設定が必要になります - Babel を使用する場合は、
babel.config.jsとjest.config.jsの両方を正しく設定してください
これらの設定を適切に行うことで、モダンな ES Modules 構文を使用しながら、Jest による快適なテスト環境を構築できます。エラーメッセージに惑わされず、プロジェクトの状況に合わせた最適な解決策を選択することが、効率的な開発につながるでしょう。
関連リンク
articleJest の “Cannot use import statement outside a module” を根治する手順
articleJest の並列実行はなぜ速い?実行キューとワーカーの舞台裏を読み解く
articleJest を可観測化する:JUnit/SARIF/OpenTelemetry で CI ダッシュボードを構築
articleJest でプロパティベーステスト:fast-check で仕様を壊れにくくする設計
articleJest expect.extend チートシート:実務で使えるカスタムマッチャー 40 連発
articleJest を Yarn PnP で動かす:ゼロ‐node_modules 時代の設定レシピ
articleWebLLM とは?ブラウザだけで動くローカル推論の全体像【2025 年版】
articleMistral とは? 軽量・高速・高品質を両立する次世代 LLM の全体像
articleOllama コマンドチートシート:`run`/`pull`/`list`/`ps`/`stop` の虎の巻
articletRPC とは?型安全なフルスタック通信を実現する仕組みとメリット【2025 年版】
articleJest の “Cannot use import statement outside a module” を根治する手順
articleObsidian プラグイン相性問題の切り分け:セーフモード/最小再現/ログの活用
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来