すずかのプログラミング勉強記

元教員からエンジニアを目指す、プログラミング勉強記録です。

Jestでテストを書いたらReferenceErrorが出た【JavaScript】

はじめに

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/exportsmodule.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を想定しており、importexportなど、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" // 追加
  }

これでテストファイル・本体ファイルともにエラーなく実行できました。

参考文献