Open8

TypeScript: JestでES Modulesは問題なくテストできるのか?

TypeScript + Node.js + ESM + Jestの組み合わせを調査する。

調査すること

  • ES Modulesをts-jestで問題なく実行できるか?

実験コード

実験のために書いたコードはGitHubに置いてある

package.jsonの設定

まず、Node.jsにパッケージがESMであると認識してもらうために、package.jsonのtype"module"にする。

package.json
 {
   "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に次の設定を加える

package.json
 {
   ...
+  "jest": {
+    "globals": {
+      "ts-jest": {
+        "useESM": true
+      }
+    },
+    "preset": "ts-jest/presets/default-esm"
+  },
   ...
 }

tsconfig.jsonの設定

tsc -initで生成したtsconfig.jsonはmodulecommonjsになっているはずなので、そこをes2020に変えてコンパイル後のJavaScriptコードがESMになるようにする:

tsconfig.json
 {
   "compilerOptions": {
     "target": "es2020",
+    "module": "es2020",
     "strict": true,
     "esModuleInterop": true,
     "skipLibCheck": true,
     "forceConsistentCasingInFileNames": true,
     "declaration": true
   }
 }

ESMのテスト対象をJestでテストする

ESMのテスト対象として次のファイルを作った:

typescript.ts
export default "TypeScript default value";

export const typescriptNamedValue: string = "TypeScript named value";

これをテストするテストコードを作った:

typescript.spec.ts
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を用意する:

javascript.js
export default "JavaScript default value";

export const javascriptNamedValue = "JavaScript named value";

これをTypeScriptからテストするコードを書く:

typescript.spec.ts
import javascriptDefaultValue, { javascriptNamedValue } from "./javascript";

test("javascript.js", () => {
  expect(javascriptDefaultValue).toMatchInlineSnapshot(
    `"JavaScript default value"`
  );
  expect(javascriptNamedValue).toMatchInlineSnapshot(
    `"JavaScript named value"`
  );
});

tsconfig.jsonの設定変更も忘れずに:

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にすればいい。

package.json
 {
   ...
   "jest": {
     "globals": {
       "ts-jest": {
         "useESM": true
       }
     },
-    "preset": "ts-jest/presets/default-esm"
+    "preset": "ts-jest/presets/js-with-ts-esm"
   },
   ...
 }

この設定で実行してみる:

npx jest

実行結果: 上手くテストできている

ログインするとコメントできます