カテゴリー
【nodejsでユニットテスト】Mocha/ChaiのESModule対応〜ビルドインTest runnerを参考に動作させてみる
※ 当ページには【広告/PR】を含む場合があります。
2023/04/15
Node.jsのユニットテスト界隈で利用されるテスト用フレームワークはいくつか選択肢がありますが、個人的に長年お世話になってきたのは
昨今、Node.jsのCommonJS離れが進み、node18からはESModuleベースのJavascriptコード設計を、開発者側も意識せざるを得ない状況になっています。
と言うことで、元々CommonjsベースのJavascriptコードのユニットテストに使われてきた実績のあるMochaですが、そのままNodejsのバージョンをv18以降に引き上げると、様々なエラーに直面してかなり面喰らってしまうことでしょう。
そんな中、長らくExprerimental扱いであったNodejs純正のユニットテストモジュール・
こちらは既にESModule対応が充実したユニットテスト機能が充実しているので、現状はストレスなく使うことができます。
Test runnerに関しては、これはこれで素晴らしいことなのですが、過去に作り溜めてきたMocha/Chai用のテストコードの数々を今更容易に捨て去ることもできずにどうすれば...
と困った時にどうにかしてみた時の内容をメモとして残します。
一旦、純正Test Runnerを使ってみる
Node.jsの新規プロジェクトを作成するような状況であれば、もはやMocha/Chaiをユニットテスト用のフレームワークとしてわざわざ導入する必要性も希薄になってくるやもしれません。
これから一から始めるのであれば
ひとまず「Test runner」を使えるNode.jsから、どんなものなのかを軽く試してみます。
Test runnerを使ってみる
「Test runner」はnode18以降でビルドインモジュールとしてそのまま呼び出して利用することができます。
まずは以下のように、何もしない「空振り」をやってみましょう。
$ node --version
v20.10.0
$ node --test
ℹ tests 0
ℹ suites 0
ℹ pass 0
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 110.593313
このように使い方は簡単で、例えばテストしたいjsコードを指定することで、
$ node --test hoge.js
とすると、この場合
hoge.js
もっと突っ込んで、Typescriptのコードはそのまま使えるのか、何も考えずTest runnerでユニットテストを実行してみると、
$ node --test hoge.ts
node:internal/modules/esm/get_format:160
throw new ERR_UNKNOWN_FILE_EXTENSION(ext, filepath);
^
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /********/hoge.ts
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:160:9)
at defaultGetFormat (node:internal/modules/esm/get_format:203:36)
at defaultLoad (node:internal/modules/esm/load:141:22)
at async ModuleLoader.load (node:internal/modules/esm/loader:409:7)
at async ModuleLoader.moduleProvider (node:internal/modules/esm/loader:291:45)
at async link (node:internal/modules/esm/module_job:76:21) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
何やらそのままでは、
「TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts"」
Test runnerでもTypescriptコードでユニットテストしたい
以下のリンクでも議論されていますが、Test runnerでTypescriptをうまく読み込ませるためのテクニックがいくつか存在しているようです。
まずはCommonJS時代からやられている
ts-node
$ node --require ts-node/register hoge.ts
TypeError: Unknown file extension ".ts" for /.../hoge.ts
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:160:9)
at defaultGetFormat (node:internal/modules/esm/get_format:203:36)
at defaultLoad (node:internal/modules/esm/load:141:22)
at async ModuleLoader.load (node:internal/modules/esm/loader:409:7)
at async ModuleLoader.moduleProvider (node:internal/modules/esm/loader:291:45)
at async link (node:internal/modules/esm/module_job:76:21) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
なんだかtypescriptファイルをそのまま読み込んでくれる気配がありません...。
と、そこで対処法としてもっとも高評価を受けていた
tsxをまだインストールしていなかったので、以下で環境に追加します。
$ yarn add -D tsx
これでtsxが使えるようになったので、Test runnerで以下のtsユニットテストを評価してみます。
import assert from 'assert/strict';
import test from 'node:test';
test('1 is equal to 1.', () => {
assert.strictEqual(1, 1);
});
ではユニットテストを実行してみます。
$ node --loader tsx hoge.spec.ts
node:internal/process/esm_loader:40
internalBinding('errors').triggerUncaughtException(
^
Error: tsx must be loaded with --import instead of --loader
The --loader flag was deprecated in Node v20.6.0 and v18.19.0
at z (file:///usr/src/app/node_modules/tsx/dist/esm/index.mjs:1:1773)
at Hooks.addCustomLoader (node:internal/modules/esm/hooks:203:24)
at Hooks.register (node:internal/modules/esm/hooks:169:16)
at async initializeHooks (node:internal/modules/esm/utils:233:5)
at async customizedModuleWorker (node:internal/modules/esm/worker:104:24)
またエラーが発生していますが、node20では
--loader
--import
オプション名を替えて再度Test runnerを叩きます。
$ node --import tsx hoge.spec.ts
✔ 1 is equal to 1. (1.355371ms)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 13.941092
問題なくユニットテストが出来ました。
Mochaでもtsxをtsローダーとして使ってみる
ではようやく本題ですが、ESModuleベースのNodejsプロジェクトでは、tsコードのトランスパイル後にMochaが正しく機能せずにエラーが起こります。
これは現状でMochaが正常に実行されるのがCommonjsのjavascriptコードを保証しているためで、出力がESModuleになっていると色々と予測できない挙動になります。
「tsx」の良いところは、CJSとESMのコンパイルを内部で良しなに処理してくれるそうで、コード開発者にトランスパイルの処理を意識させることなく使えるように設計されている点にあります。
そこで、先程Test runnerで使ったテクニックをそのままMochaでも利用して、既存のMocha/Chaiベースのユニットテストコードに適用できるか試してみます。
$ mocha --import=tsx test/*.spec.ts
#...ユニットテストの内容は省略
1 passing (7ms)
Done in 2.46s.
手元の環境ではそのままtsxをMochaのtsローダーとして利用することができました。 めでたしめでたし。
まとめ
今回はMochaとtsxの組み合わせでNodejsのv18以降のESMベースプロジェクトでもそのままユニットテストが通るのか否か即席で試してみました。
大量にあるMocha/Chaiのユニットテストを今更ながら他のユニットテストのフレームワークへ以降させるのはかなり苦労するため、こういった救済措置があるのは助かりました。
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー