はじめに
※この記事はAI生成です。自分の理解のためにまとめてもらいました。
Vite・React・TypeScriptで作成したプロジェクトへ、Jestを導入した時の自分メモです。
今回のプロジェクトは、package.jsonに次の設定があるESM構成です。
{
"type": "module"
}
そのため、通常のCommonJS向けJest設定ではなく、ESMを扱えるようにts-jestを設定する必要がありました。
また、TypeScript設定で次の組み合わせを使用しています。
{
"module": "ESNext",
"moduleResolution": "bundler"
}
この記事では、Vite用とJest用のTypeScript設定を分離し、Jestが起動するところまでをまとめます。
環境
今回使用した主なバージョンです。
Vite: 8.1.1
React: 19.2.7
TypeScript: 6.0.2
Jest: 30.4.2
ts-jest: 29.4.12
jest-environment-jsdom: 30.4.1
結論
Vite用とJest用で、TypeScript設定を分けました。
tsconfig.json
├── tsconfig.app.json # React・Vite用
├── tsconfig.node.json # vite.config.ts用
└── tsconfig.jest.json # Jest用
Vite用のmoduleResolution: "bundler"を維持しつつ、Jestでは専用のtsconfig.jest.jsonを読み込ませます。
必要なパッケージをインストールする
JestとReact Testing Libraryに必要なパッケージをインストールします。
npm install -D \
jest \
ts-jest \
@types/jest \
jest-environment-jsdom \
identity-obj-proxy \
@testing-library/react \
@testing-library/jest-dom \
@testing-library/user-event
それぞれの役割は次のとおりです。
| パッケージ | 役割 |
|---|---|
jest |
テストを実行する本体 |
ts-jest |
TypeScriptをJestで実行できる形式へ変換する |
@types/jest |
testやexpectなどの型定義 |
jest-environment-jsdom |
Node.js上にブラウザ風の環境を作る |
identity-obj-proxy |
CSSのimportをテスト中に置き換える |
@testing-library/react |
Reactコンポーネントをテストする |
@testing-library/jest-dom |
DOM用のMatcherを追加する |
@testing-library/user-event |
ユーザー操作を再現する |
Vitestから切り替える場合は、先に削除します。
npm uninstall -D vitest jsdom
jsdomを直接インストールする代わりに、Jestではjest-environment-jsdomを使用します。
ルートのtsconfig.json
ルートのtsconfig.jsonには、アプリ用とNode.js用の参照だけを記述します。
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
ルートへJest用の設定を直接追加すると、Vite用の設定と役割が重複するため、Jest用の設定は別ファイルへ分けます。
Vite用のtsconfig.app.json
Viteでは、次の組み合わせを使用します。
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler",
"noEmit": true,
"jsx": "react-jsx"
}
}
moduleResolutionとは
moduleResolutionは、TypeScriptがimport先のファイルやパッケージを探す方法です。
{
"moduleResolution": "bundler"
}
bundlerは、Viteのようなバンドラーを使うプロジェクトに適しています。
以前の"moduleResolution": "Node"は、現在では古いnode10方式を意味します。
TypeScript公式ドキュメントでも、Viteのようなバンドラーを使用する場合はbundlerが用意されています。
noEmitとは
{
"noEmit": true
}
noEmitは、TypeScriptにJavaScriptファイルを生成させない設定です。
Viteプロジェクトでは、TypeScriptは型チェックを担当し、実際の変換やビルドはViteが担当します。
Jest専用のtsconfig.jest.json
プロジェクト直下へtsconfig.jest.jsonを作成します。
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"types": [
"vite/client",
"node",
"jest",
"@testing-library/jest-dom"
],
"noEmit": true
},
"include": [
"src",
"jest.setup.ts"
]
}
各設定の意味
extends
{
"extends": "./tsconfig.app.json"
}
ViteアプリのTypeScript設定を土台として再利用します。
module
{
"module": "ESNext"
}
TypeScriptのimport・exportをESMとして扱います。
types
{
"types": [
"vite/client",
"node",
"jest",
"@testing-library/jest-dom"
]
}
以下の型を利用できるようにします。
- Viteの
import.meta.env - Node.js
- Jest
- Testing LibraryのDOM Matcher
jest.config.js
jest.config.jsをプロジェクト直下へ作成します。
// TypeScriptをESMとして変換するJest設定を生成します。
import { createDefaultEsmPreset } from "ts-jest";
// Jest専用のtsconfigをts-jestへ渡します。
const tsJestPreset = createDefaultEsmPreset({
tsconfig: "./tsconfig.jest.json",
});
export default {
// ts-jestが生成したtransformやESM設定を展開します。
...tsJestPreset,
// documentやwindowを利用できるブラウザ風環境です。
testEnvironment: "jsdom",
// 各テストファイルの実行前に読み込むファイルです。
setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
// CSSをテスト中にJavaScriptとして実行しないようにします。
moduleNameMapper: {
"\\.(css|less)$": "identity-obj-proxy",
},
};
preset: "ts-jest"だけを指定した場合、標準ではCommonJSとして変換されます。
今回はESMプロジェクトなので、createDefaultEsmPreset()を使ってESM用の設定を生成しています。
ts-jestのESM設定については、公式ドキュメントで確認できます。
jest.setup.ts
プロジェクト直下へjest.setup.tsを作成します。
// toBeInTheDocument()などのDOM用Matcherを追加します。
import "@testing-library/jest-dom";
これにより、テスト内で次のような確認ができます。
expect(element).toBeInTheDocument();
expect(element).toBeVisible();
expect(element).toHaveTextContent("保存");
dotenvを使用する場合
テスト実行前に.envを読み込む場合は、次のように書けます。
import "@testing-library/jest-dom";
import { config } from "dotenv";
// .envの値をprocess.envへ読み込みます。
config();
ただし、dotenvが読み込むのはprocess.envです。
Viteの次の書き方とは別物なので注意が必要です。
import.meta.env.VITE_API_URL
import.meta.envを使うコードのテストでは、別途モックなどを検討する必要があります。
package.jsonへテストコマンドを追加する
package.jsonのscriptsへ、次のコマンドを追加します。
{
"scripts": {
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
"test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch"
}
}
Jest 30のESM対応では、--experimental-vm-modulesを付けて起動します。
通常のテスト実行は次のコマンドです。
npm test
監視モードで実行する場合はこちらです。
npm run test:watch
テストがない状態で起動確認する
まだテストファイルを作成していないため、次のコマンドで起動確認しました。
npm test -- --passWithNoTests
実行結果は次のようになりました。
> type-class@0.0.0 test
> node --experimental-vm-modules node_modules/jest/bin/jest.js --passWithNoTests
No tests found, exiting with code 0
No tests foundはエラーではない
今回のコマンドには、次のオプションを付けています。
--passWithNoTests
このオプションは、テストが0件でも正常終了させる指定です。
そのため、次の表示はJestの起動成功を意味します。
exiting with code 0
現時点で確認できたこと
今回確認できたのは、次の範囲です。
- Jest本体を起動できる
-
jest.config.jsを読み込める - ESM用の
ts-jest設定を読み込める - テストが0件でも正常終了できる
まだ実際のテストファイルを作っていないため、次の項目は未確認です。
- TypeScriptのテストを実行できるか
- Reactコンポーネントをレンダリングできるか
- CSS importを処理できるか
-
jest-domのMatcherを使えるか -
import.meta.envを使用するコードをテストできるか
まとめ
今回のポイントは、Vite用とJest用のTypeScript設定を分けることでした。
Vite
└── tsconfig.app.json
├── module: esnext
└── moduleResolution: bundler
Jest
└── tsconfig.jest.json
├── tsconfig.app.jsonを継承
└── ts-jestのESM設定で使用
"module": "ESNext"と"moduleResolution": "bundler"の組み合わせ自体は問題ありません。
問題になりやすいのは、Vite用・Node.js用・Jest用の設定を、すべてルートのtsconfig.jsonへ混在させることです。
用途ごとに設定ファイルを分けることで、それぞれの役割を理解しやすくなり、設定同士の衝突も避けやすくなります。