はじめに
JavaScriptで「FizzBuzz」のプログラムを作り、Jestでテストを書いていた際に、ReferenceErrorが出てて詰まりました。調べたことの理解を深めるため、解決した手順をまとめます。
実行環境
- macOS:Ventura v13.3.1
- Node.js:v18.18.0
- npm:v9.8.1
- Jest:v29.7.0
やったこと
- 「fizzbuzz.js」にプログラムを書く。
- 公式ドキュメント(はじめましょう · Jest)の手順に従って、
npm install --save-dev jest
でJestを導入。 - 「package.json」に以下を追記。
{ "scripts": { "test": "jest" }
- テストコードを作成する。
const fizzbuzz = require('./fizzbuzz.js'); // 各項目のテスト
- 本体のコードを書き換える。
const fizzbuzz = (n) => { // n・"Fizz"・"Buzz"・"FizzBuzz"のどれかを表示するコード }; module.exports = fizzbuzz; // これを追加
npm test
をすると、テストコードはエラー無しでテストができました。
しかし、本体のコードを実行時すると、ReferenceErrorが出ました。
エラーの場所と中身
- エラーが出た箇所
「fizzbuzz.js」のmodule.exports = fizzbuzz;
- エラーの中身
ReferenceError: module is not defined in ES module scope This file is being treated as an ES module because it has a '.js' file extension and 'ファイルのパス/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
和訳:
ReferenceError: モジュールが ES モジュール スコープで定義されていません。
このファイルは「.js」ファイル拡張子があり、「ファイルのパス/package.json」に"type": "module".
が含まれているため、ES moduleとして扱われます。 CommonJS スクリプトとして扱うには、「.cjs」ファイル拡張子を使用するように名前を変更してください。
調べたこと
エラー文の内容が理解できなかったので、JavaScriptのモジュールシステムについて調べました。 ESModulesとして扱われるファイルに、CommonJSの書き方を混ぜたのがエラーの原因でした。
長いので折りたたみ
モジュールって何?
- 変数や関数などをまとめたもの。
- JavaScript のモジュールシステムには、CommonJS ・AMD ・ESModulesなどが存在する。
- 変数や関数などを外部にエクスポートできる。エクスポートされた変数や関数などは、インポートして外部から利用できる。
CommonJS (CJS)
- サーバーサイドなどにおけるJavaScriptの標準仕様の策定を目的としている。
- Node.jsはデフォルトで全てのモジュールをCommonJSで扱う。
require
/exports
・module.exports
キーワードを使用してモジュールを読み込む。ESModules (ESM)
- ECMAScript 2015 (ES6) から導入された、JavaScriptの標準的なモジュールシステム。
- Webブラウザがデフォルトで採用。
- Node.jsは
"type": "module"
を設定すると、ESModulesに変えることができる。 import
/export
キーワードを使用してモジュールを読み込む。
ESModulesとCommonJSを混ぜちゃダメ!
Fizzbuzzのプログラムは、package.jsonに"type": "module"
と記載がありました。そのため、本体ファイルはESModulesとして扱われます。
しかしテストを実行できるようにするため、module.exports
というCommonJSの書き方を混ぜたため、エラーが出ました。
試したこと
エラー文には本体のコードをCommonJS スクリプトとして扱う方法が書いてありましたが、現在はESModulesが主流になりつつあるようなので、ESModulesのままにすることにしました。
全てESModulesの書き方に変更
- 本体のコードを書き換える。
module.exports
していた部分を以下に変更。
export default fizzbuzz;
- テストファイルを書き換える。
require
をしていた部分を以下に変更。
import fizzbuzz from "./fizzbuzz.js";
上の修正で本体のファイルのエラーを解決できました。しかし、テストファイルの方でSyntaxErrorが出ました😅
エラー文
❯ npm test > test > jest FAIL ./fizzbuzz.test.js ● Test suite failed to run Jest encountered an unexpected token Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax. (略) SyntaxError: 'ファイルのパスfizzbuzz.test.js: Unexpected token (1:21)
JestはデフォルトではCommonJSを想定しており、import
やexport
など、ESModulesの構文をサポートしていないことが原因でした。
Node.jsの実行オプションを指定
JestがESModulesをサポートするためには、 Node.jsの実行オプションを指定する必要があります。公式ドキュメント(ECMAScript モジュール · Jest)に記載があったので、その手順で書き換えました。
- 「package.json」を書き換える。
"scripts": { "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" // 追加 }
または
"scripts": { "test": "NODE_OPTIONS=--experimental-vm-modules jest" // 追加 }
これでテストファイル・本体ファイルともにエラーなく実行できました。
参考文献
CommonJSとESModules
ES Modulesの構文
JestとESModulesについて