TypeScript: JestでES Modulesは問題なくテストできるのか?
TypeScript + Node.js + ESM + Jestの組み合わせを調査する。
調査すること
- ES Modulesをts-jestで問題なく実行できるか?
 
実験コード
実験のために書いたコードはGitHubに置いてある
package.jsonの設定
まず、Node.jsにパッケージがESMであると認識してもらうために、package.jsonのtypeを"module"にする。
 {
   "name": "typescript-nodejs-esm-jest",
   "version": "1.0.0",
   "private": true,
   "license": "MIT",
+  "type": "module",
  ...
 }
TypeScriptをts-jestで実行するための環境構築
これはESM関係なくTypeScriptのコンパイル手順なしにテストコードを実行するために必要。
pnpm add -D typescript jest @types/jest ts-jest
ts-jestでESMを使えるようにする設定
公式ドキュメント: ESM Support | ts-jest
package.jsonに次の設定を加える
 {
   ...
+  "jest": {
+    "globals": {
+      "ts-jest": {
+        "useESM": true
+      }
+    },
+    "preset": "ts-jest/presets/default-esm"
+  },
   ...
 }
tsconfig.jsonの設定
tsc -initで生成したtsconfig.jsonはmoduleがcommonjsになっているはずなので、そこをes2020に変えてコンパイル後のJavaScriptコードがESMになるようにする:
 {
   "compilerOptions": {
     "target": "es2020",
+    "module": "es2020",
     "strict": true,
     "esModuleInterop": true,
     "skipLibCheck": true,
     "forceConsistentCasingInFileNames": true,
     "declaration": true
   }
 }
ESMのテスト対象をJestでテストする
ESMのテスト対象として次のファイルを作った:
export default "TypeScript default value";
export const typescriptNamedValue: string = "TypeScript named value";
これをテストするテストコードを作った:
import typescriptDefaultValue, { typescriptNamedValue } from "./typescript";
test("typescript.ts", () => {
  expect(typescriptDefaultValue).toMatchInlineSnapshot(
    `"TypeScript default value"`
  );
  expect(typescriptNamedValue).toMatchInlineSnapshot(
    `"TypeScript named value"`
  );
});
Jestを動かす:
npx jest
実行結果:
TypeScriptのテストコードでJavaScriptのESMをテストできるのか?
TypeScriptのテストコードでTypeScriptのESMはテストできることが分かった。
では、TypeScriptのテストコードでJavaScriptのESMはテストできるのだろうか?
JavaScriptのESMを用意する:
export default "JavaScript default value";
export const javascriptNamedValue = "JavaScript named value";
これをTypeScriptからテストするコードを書く:
import javascriptDefaultValue, { javascriptNamedValue } from "./javascript";
test("javascript.js", () => {
  expect(javascriptDefaultValue).toMatchInlineSnapshot(
    `"JavaScript default value"`
  );
  expect(javascriptNamedValue).toMatchInlineSnapshot(
    `"JavaScript named value"`
  );
});
tsconfig.jsonの設定変更も忘れずに:
 {
   "compilerOptions": {
     "target": "es2020",
     "module": "es2020",
     "strict": true,
     "esModuleInterop": true,
     "skipLibCheck": true,
     "forceConsistentCasingInFileNames": true,
     "declaration": true,
+    "allowJs": true
   }
 }
実行する:
npx jest
結果: 「Jest encountered an unexpected token」というエラーが発生してFAILとなった
TypeScriptのテストコードでJavaScriptのESMをテストする設定
TypeScriptのテストコードでJavascriptのESMをテストするには、ts-jestの設定を変える必要があるようだ。presetをts-jest/presets/js-with-ts-esmにすればいい。
 {
   ...
   "jest": {
     "globals": {
       "ts-jest": {
         "useESM": true
       }
     },
-    "preset": "ts-jest/presets/default-esm"
+    "preset": "ts-jest/presets/js-with-ts-esm"
   },
   ...
 }
この設定で実行してみる:
npx jest
実行結果: 上手くテストできている