Node.js(ES6で実装)でJestによるテストを実行するためのBabel・ESLintの設定とは?

はじめに

こんにちは、SHIFT の開発部門に所属しているKatayamaです。

Node.js で ES Modules を利用する時に、Babel によるトランスパイルを利用しているような場合、そのまま Jest によるテストを実行すると以下のようなエラー("Cannot use import statement outside a module")が発生すると思います。

[root@localhost node-express]# yarn test
yarn run v1.22.17
$ jest
 FAIL  tests/sum.test.js
  ● Test suite failed to run

    ...

    Details:

    /root/workspace/node-express/tests/sum.test.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import sum from '../src/sum';
                                                                                      ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1728:14)
      at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:333:13)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.723 s
Ran all test suites.
error Command failed with exit code 1.

今回は Node.js で ES Modules を利用するのに、Babel によるトランスパイルを利用している時の Jest の設定方法についてみていきたいと思います。

また、Project に ESLint を設定してる場合、Jest でテストを書くと、以下のようにエラー(Jest の構文に対する "no-undef")が表示されてしまいます。このエラーを解消する方法についてもみていきたいと思います。

※Node.js で ES Modules を利用するための設定については、Node.js で import・export(ES6 の構文)を使えるように webpack × Babel の設定をやってみたを参照ください。

Jest でテスト実行できるように設定する

結論:どうすればいいか?

Babel の設定ファイル(今回は babel.config.js)に、以下のような設定を行う。

// babel.config.js
module.exports = {
  presets: [["@babel/preset-env", { targets: { node: "current" } }]],
};

※既に Babel の設定がある場合、既存の設定と競合しないようにするために以下のように process.env.NODE_ENV=test の時だけ有効になる設定の書き方をする。

// babel.config.js
module.exports = {
	presets: [
		[
			'@babel/preset-env',
			{
				...
			}
		]
	],
	env: {
		test: {
			presets: [
                [
                    '@babel/preset-env',
                    {
                        targets: { node: 'current' }
                    }
                ]
            ]
		}
	}
};


設定について

基本的には Jest の公式「Babel を使用する」に書かれている通りに設定すればいい。まずは必要なものをインストールする。

yarn add --dev jest babel-jest @babel/core @babel/preset-env

一点、"babel-jest"ついてだが、jestがコードを解釈するときに実行されるトランスパイル時の設定が、プロジェクトに存在する Babel の設定(babel.config.jsなど)を読み込むだけであれば、"yarn add --dev"で明示的にdevdependencyに追加する必要性はない。理由としては jest のインストール時に自動的に node_modules に"babel-jest"が追加されるようになっているため。そのことは公式にも

注意:babel-jest は Jest のインストール時に自動的にインストールされ、...

と確かに書かれている事からもわかる。ちなみに、本当にjestをインストールしただけで"babel-jest"が node_modules に追加されるのか?だが、それは package.lock.json or yarn.lock を確認すれば babel-jest が入っている事が確認できる。
後は Babel の configration(babel.config.js など)に以下を記述すれば OK。

// babel.config.js
module.exports = {
  presets: [["@babel/preset-env", { targets: { node: "current" } }]],
};

※上記のように設定する際に、既に Babel の設定がある場合には、既存の設定と上記の設定をどのように共存させるのか?という疑問が出てくる。この場合には、Babel の設定オプションのenvを使って、環境変数 NODE_ENV の値で有効化する設定を変える事で対応できる。具体的には、Jest 実行時には NODE_ENV=test になるので、以下のように babel.config.js に test 用の設定を追加する。

// babel.config.js
module.exports = {
	presets: [
		[
			'@babel/preset-env',
			{
				...
			}
		]
	],
	env: {
		test: {
			presets: [
                [
                    '@babel/preset-env',
                    {
                        targets: { node: 'current' }
                    }
                ]
            ]
		}
	}
};

※Jest 実行時には NODE_ENV=test になるというのは、公式の以下の記述から確認できる。

Jest will set process.env.NODE_ENV to 'test' if it's not set to something else.

Babel の設定後に Jest でテスト を実行してみる

問題なくテスト実行できるようになった事が確認できる。

[root@localhost node-express]# yarn test
yarn run v1.22.17
$ jest
 PASS  tests/sum.test.js
  ✓ adds 1 + 2 to equal 3 (499 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.92 s
Ran all test suites.
Done in 3.28s.

テスト対象のコードは以下(ES6 以降の構文である async/await をわざと入れたコードにしている)。

sum.js
export default async (a, b) => {
	await new Promise((resolve) => {
		setTimeout(() => {
			resolve('sleep');
		}, 500);
	});
	return a + b;
};

テストコードは以下。

import sum from '../src/sum';

test('adds 1 + 2 to equal 3', async () => {
	expect(await sum(1, 2)).toBe(3);
});

・参考:ES6+Babel7 環境で Jest する方法

ESLint でエラーが発生しないように設定をする

結論:どうすればいいか?

.eslintrc.json に以下のように"plugin:jest/recommended"を extends に追加する。

// .eslintrc.json
{
	...
	"extends": [
		...
		"plugin:jest/recommended",
		...
	],
	...
}

設定について

eslint-plugin-jestをインストールして、extends に"plugin:jest/recommended"を追加する。

こうする事で、以下のようにtestやexpectでエラーが表示されなくなる。また、eslint-plugin-jest で定義されているルールに基づき新たに warn が出るようになり、ちゃんと ESLint の設定がうまく動いている事が分かる(after の方には ESLint が動いている事を確認する意味で、わざと warn が表示されるような実装を追加している)。

※補足として、公式のUsageを見ると、"plugins": ["jest"]や"env": { "jest/globals": true }を .eslintrc.json に追加するように書いてあるが、今回そうではなく extends に追加するだけにしたのは、eslint-plugin-jest の ESLint の設定が書かれているindex.tsに既に

plugins: ['jest'],
env: { 'jest/globals': true },

という設定が書かれており、この設定は (extends に"plugin:jest/recommended"のようにする事で)recommended: createConfig(recommendedRules)の部分から自身の .eslintrc.json に追加されるようになっているため。

※eslint-plugin-jest には、"plugin:jest/recommended"以外にも、"plugin:jest/style""plugin:jest/all"という設定もあり、要求に応じて使い分けも可能。

まとめとして

今回は Node.js で Babel によるトランスパイルを利用しているような場合に、Jest を実行するために必要な設定と、Jest でテストを実装する際に出てしまう ESLint のエラーを表示されないようにする設定についてみてきた。上記のような設定で快適に Jest によるテストを書いていく事ができると思う。


執筆者プロフィール:Katayama Yuta
SaaS ERPパッケージベンダーにて開発を2年経験。 SHIFTでは、GUIテストの自動化やUnitテストの実装などテスト関係の案件に従事したり、DevOpsの一環でCICD導入支援をする案件にも従事。 最近開発部門へ異動し、再び開発エンジニアに。座学で読み物を読むより、色々手を動かして試したり学んだりするのが好きなタイプ。

お問合せはお気軽に
https://service.shiftinc.jp/contact/

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/