📖

JestをES6化してimport/exportを使う方法

2021/04/26に公開
2

NODE_OPTIONS: experimental-vm-modulesを使う

関連記事:

Jestのサンプルコードを見ると
(。´・ω・)ん?
と思うことがある。

sum.js
function sum(a, b) {
  return a + b;
}
module.exports = sum;
sum.test.js
const sum = require('./sum');

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

(・・? なんでCommonJS方式なの?

exports/requireがCommonJS、
一方import/exportがES6だ

世の中ES6で動いてるのに、
Jestだけ置いてけぼりになっている状態なの?
ちょっと調べてみたが、
NodeJSの仕様に合わせてそうしているようだ。

それはちょっとなぁ~と思ったら、
どうやら最近サポートされたようだ。

NodeJS側で (^_^;)

正確にはNodeJSのexperimental-modulesオプションにて対応していたが、
v13.2.0でそれが外れpackage.jsonのtype定義、あるいは--input-typeオプション、
拡張子をmjsにするなどでES Moduleが使えるようになるそうだ。

ただ、これとJestでのサポートは別のようで、
experimental-vm-modulesオプションは必須。

ヾ(・ω<)ノ" 三三三● ⅱⅲ コロコロ♪


------------------- ↓ 本題はここから ↓-------------------

ESモジュール化

こちらを参照

https://jestjs.io/ja/docs/ecmascript-modules

NODE_OPTIONSを設定

NODE_OPTIONSにexperimental-vm-modulesというオプションを追加する。
Windowsユーザーはcross-envを使うと良いらしい

https://nodejs.org/api/cli.html#cli_experimental_vm_modules

特定のコマンドに追加するほうがいいので以下のように設定

.bashrc
export NODE_OPTIONS=--experimental-vm-modules

反映

source ~/.bashrc

確認

env | grep -i 'node'
NODE_OPTIONS=--experimental-vm-modules

package.jsonの調整

package.json上のtypeディレクティブにモジュールを追記する。
拡張子をmjsとしてる場合でも必要っぽい

package.json
{
・・・
  "type": "module"
}  

コード調整

サンプルをimport/exportに書き換える
defaultに名前つける意味はちょっとわからん。

sum.js
const sum = (a, b) => {
  return a + b
}
export default sum
sum.test.js
import sum from './sum.js'

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

テスト実行

npx jest sum.test.js
(node:9379) ExperimentalWarning: VM Modules is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
 PASS  ./sum.test.js
  ✓ adds 1 + 2 to equal 3 (1 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.479 s
Ran all test suites matching /sum.test.js/i.

問題なさそう


------------------- ↓ 後書はここから ↓-------------------

package.jsonの調整

上記は.bashrcで調整したが、
CIなどに加えることを考えるとコマンド自体を弄るほうがいい。

package.json
  "scripts": {
    "test": "NODE_OPTIONS=--experimental-vm-modules npx jest",
    "test.watch": "NODE_OPTIONS=--experimental-vm-modules npx jest --watchAll"
  },
npm test sum.test.js

moduleが定義されてないと怒られる場合

既存のテストをmoduleに変更すると以下のようなエラーが出る。

NODE_OPTIONS=--experimental-vm-modules npx jest sum.test.js
ReferenceError: module is not defined
    at file:///home/dozo/tmp/tests2/jest.config.js:6:1
    at ModuleJob.run (node:internal/modules/esm/module_job:154:23)
    at async Loader.import (node:internal/modules/esm/loader:166:24)
    at async importModuleDynamicallyWrapper (node:internal/vm/module:437:15)
    at async readConfigFileAndSetRootDir (/home/dozo/tmp/tests2/node_modules/jest-config/build/readConfigFileAndSetRootDir.js:126:32)
    at async readConfig (/home/dozo/tmp/tests2/node_modules/jest-config/build/index.js:217:18)
    at async readConfigs (/home/dozo/tmp/tests2/node_modules/jest-config/build/index.js:406:26)
    at async runCLI (/home/dozo/tmp/tests2/node_modules/@jest/core/build/cli/index.js:230:59)
    at async Object.run (/home/dozo/tmp/tests2/node_modules/jest-cli/build/cli/index.js:163:37)

これはjest.config.jsがCommonJS形式で書かれているのが原因

これを

jest.config.js
module.exports = {
・・・・ 
}

こう書き換えよう

jest.config.js
export default {
・・・・  
}

jest.config.jsを削除してjest --initを打ち直してもいい
その際ファイル名はjest.config.mjsとなっている

ExperimentalWarningが気になる

Jest実行すると以下のワーニングが常に出てくる

(node:5995) ExperimentalWarning: VM Modules is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)

まぁ、一行だけならいいのだが、
テストの数だけ出てくるので結構うっとうしい。
方法としてはwarningを消すことだろう。

NODE_OPTIONS='--experimental-vm-modules --no-warnings' npx jest

開発でワーニング消すなど以ての外だが、
テストランナーだけだから問題はないだろう。

Discussion

standard softwarestandard software

不要な情報かもしれませんが、
私は、テストコードの方も、プロダクトコードと同じように
WebPack(やBabel)でビルドしてからJestに流すというようにしています。

そうすると、node は CommonJS 前提ですが、自分で CommonJS 書く必要がなくなり
テストコード側が ESModules 使っても問題なく
プロダクトと同じ仕組みで同じ構文が使えたりします。

dozodozo

プロダクトと揃えるのならばそれでいいと思います。