はじめに
この記事では以下のことについて説明しています。
- JestでTypeScirptの関数をテストする方法
- Jest(ts-jest)のESM対応とモジュール解決方法
Jestの概要や書き方については説明しません。公式サイトを参考にしてください。
Jestは JavaScript のテスティングフレームワークです。TypeScriptのコードは Jest だけではテストできません。
以下のものが必要です。
- Jest
- ts-jest
- @types/jest
ts-jest
によって TypeScript で書いたテストコードを直接テスト実行することができます。@types/jest
は Jest のAPIの型定義ファイルです。
今回紹介するケースは ESM対応とモジュールインポートを実施したテストです。
必要なライブラリのインストール
npm install --save-dev jest ts-jest @types/jest
jest.config.json
の作成(TypeScript用)
npx ts-jest config:init
実行すると以下の jest.config.json
が出力されます。
/** @type {import('ts-jest').JestConfigWithTsJest} **/
export default {
testEnvironment: "node",
transform: {
"^.+.tsx?$": ["ts-jest",{}],
},
};
テストの作成 / 実施
今回は以下のケースで説明していきます。
.
├── src
│ ├── hoge
│ │ └── hoge.ts
│ └── index.ts
├── test
│ └── hoge.test.ts
├── package.json
├── jest.config.js
└── tsconfig.json
test
ディレクトリのコードも TypeScript のコンパイル対象に含めたいので、 include
にその設定を追記しましょう。
{
"compilerOptions": {
"target": "es2022",
"module": "node16",
"moduleResolution": "node16",
"baseUrl": "./src",
"paths": {
"@/*": ["./*"]
},
"rootDirs": ["./src"],
"outDir": "./dist",
"sourceMap": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
},
"include": ["src/**/*", "test/**/*"], // "test/**/*" を追加する
"exclude": ["dist", "node_modules"]
}
上記の ./src/hoge/hoge.ts
のテストを書いてみます。
export const hoge: () => string = () => "hogehoge";
test("check", () => {
console.log("OK");
});
以下のコマンドを pakcage.json
に追加します。
"scripts": {
"test": "jest"
},
$ npm run test
> advent-calendar-2024@1.0.0 test
> jest
console.log
OK
at Object.<anonymous> (test/hoge.test.ts:2:13)
PASS test/hoge.test.ts
✓ check (14 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.62 s
Ran all test suites.
無事テストを実行することができました。
ESM対応をする
例えば以下のようなモジュールインポートを行うテストを書いたとしましょう。
import { hoge } from '@/hoge/hoge';
test("check", () => {
const hogehoge = hoge();
expect(hogehoge).toBe("hogehoge");
});
これで npm run test
を実行するとテストは成功しますが、tsconfig.json
で "module": "node16"
を指定しているためIDEのエラーチェックに引っかかります。
import { hoge } from '@/hoge/hoge'
の部分で赤い波線と
Cannot find module '@/hoge/hoge' or its corresponding type declarations.ts(2307)
というエラーメッセージが表示されます。
これは末尾に.js
と拡張子を指定すると消えます。が、実行すると以下のようなエラーが出ます。
$ npm run test
> advent-calendar-2024@1.0.0 test
> jest
FAIL test/hoge.test.ts
● Test suite failed to run
Configuration error:
Could not locate module @/hoge/hoge.js mapped as:
/Users/usr0302216/local/advent-calendar-2024/src/$1.
Please check your configuration for these entries:
{
"moduleNameMapper": {
"/^@\/(.*)$/": "/Users/xxx/local/advent-calendar-2024/src/$1"
},
"resolver": undefined
}
> 1 | import { hoge } from '@/hoge/hoge.js';
| ^
2 |
3 | test("check", () => {
4 | const hogehoge = hoge();
at createNoMappedModuleFoundError (node_modules/jest-resolve/build/resolver.js:759:17)
at Object.<anonymous> (test/hoge.test.ts:1:1)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 1.094 s, estimated 2 s
Ran all test suites.
モジュールインポートに失敗しているようです。
ts-jest を使う上でESM対応の方法は
- ESM対応プラグインの導入
-
node --experimental-vm-modules node_modules/jest/bin/jest.js
で実行する -
jest.config.js
にESM対応のコードを書く
など様々な方法がありますが、1番目の「ESM対応プラグインの導入」を取ります。
プラグインのインストール
npm install --save-dev ts-jest-resolver
jest.config.json
に resolver: "ts-jest-resolver"
を追加
/** @type {import('ts-jest').JestConfigWithTsJest} **/
export default {
testEnvironment: "node",
transform: {
"^.+.tsx?$": ["ts-jest",{}],
},
resolver: "ts-jest-resolver",
};
しかし、これでも実行すると依然としてモジュール解決できずエラーが発生します。
$ npm run test
> advent-calendar-2024@1.0.0 test
> jest
FAIL test/hoge.test.ts
● Test suite failed to run
Cannot find module '@/hoge/hoge.js' from 'test/hoge.test.ts'
> 1 | import { hoge } from '@/hoge/hoge.js';
| ^
2 |
3 | test("check", () => {
4 | const hogehoge = hoge();
at Resolver._throwModNotFoundError (node_modules/jest-resolve/build/resolver.js:427:11)
at Object.<anonymous> (test/hoge.test.ts:1:1)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 1.12 s, estimated 2 s
Ran all test suites.
実は Jest は何も設定していない状態で tsconfig.json
の paths
と bashUrl
の設定を読み込むことができません。jest.config.json
の moduleNameMapper
オプションにその設定を伝える必要があります。
jest.config.json
に以下を追記しましょう。
/** @type {import('ts-jest').JestConfigWithTsJest} **/
export default {
testEnvironment: "node",
transform: {
"^.+.tsx?$": ["ts-jest",{}],
},
moduleNameMapper: {
"^@/(.*)$": '<rootDir>/src/$1',
},
resolver: "ts-jest-resolver",
};
これで実行してみましょう。
$ npm run test
> advent-calendar-2024@1.0.0 test
> jest
PASS test/hoge.test.ts
✓ check (2 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.058 s
Ran all test suites.
これで無事テストを通すことができました。
以上、Jestを使ってTypeScriptの関数をテストする方法を紹介しました。
次回はもう一回くらいテストのことについて書いてみようと思います。
おまけ
「言った通りにやったのに上手くいかないじゃん!!」という方もいらっしゃると思います。
一度テストディレクトリの名前を確認して見てください。 ディレクトリ名が間違っている可能性があります。tests
(複数形)になっていますか? test
(単数系)になっていますか?
僕はこれで2時間くらい溶かしました。