Contents
1. Jest の概要と導入メリット
Jest は Facebook が開発した、設定不要(ゼロコンフィグ)で動作する JavaScript テストランナーです。Babel・TypeScript の変換を自動的に行い、スナップショットテストやモック機能が標準装備されているため、フロントエンドだけでなく Node.js アプリケーションでもシームレスに採用できます。
| 特徴 | 具体的な効果 |
|---|---|
| ゼロコンフィグ | npm test だけでテストが走り、設定ファイル不要(ただしプロジェクト固有の要件がある場合はオプションで拡張)【1】 |
| スナップショットテスト | UI コンポーネントの出力を保存・比較でき、リグレッション防止に有効 |
内蔵モック API (jest.mock) |
外部依存やファイルシステムを簡単に置き換え、ユニットテストの信頼性が向上【1】 |
ポイント:Jest が提供する 3 本柱(設定不要・スナップショット・モック)により、チーム全体でテスト文化を早期に浸透させやすくなります。
2. プロジェクトへのインストールと基本設定
2‑1. 共通インストール手順(Node.js)
|
1 2 3 4 5 6 |
# npm を使用する場合 npm install --save-dev jest # Yarn を使用する場合 yarn add --dev jest |
package.json にテストスクリプトを追加すれば、npm test だけで Jest が起動します。
|
1 2 3 4 5 6 |
{ "scripts": { "test": "jest" } } |
2‑2. 最小構成の jest.config.js
Node 環境のみで実行する場合は、次のようにシンプルに記述できます。
|
1 2 3 4 5 6 7 |
// jest.config.js module.exports = { testEnvironment: 'node', // Node 用テスト環境 transform: {}, // Babel/TS が不要なときは空オブジェクト moduleFileExtensions: ['js', 'json'], }; |
2‑3. フロントエンド(React / Vue)向けの拡張設定
React プロジェクト
|
1 2 3 |
npm install --save-dev @babel/preset-env @babel/preset-react babel-jest identity-obj-proxy # ※ CSS/画像モックに必要なパッケージは下記参照 |
babel.config.js
|
1 2 3 4 |
module.exports = { presets: ['@babel/preset-env', '@babel/preset-react'], }; |
jest.config.js
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
module.exports = { testEnvironment: 'jsdom', transform: { '^.+\\.[jt]sx?$': 'babel-jest', }, moduleNameMapper: { // CSS モジュールを文字列に置き換えるモック '\\.(css|scss)$': 'identity-obj-proxy', // 画像ファイルは簡易的なスタブに置き換え '\\.(png|jpg|jpeg|svg)$': '<rootDir>/__mocks__/fileMock.js', }, }; |
Vue プロジェクト
|
1 2 |
npm install --save-dev @vue/cli-plugin-unit-jest vue-jest babel-jest |
jest.config.js
|
1 2 3 4 5 6 7 8 |
module.exports = { preset: '@vue/cli-plugin-unit-jest', transform: { '^.+\\.vue$': 'vue-jest', // .vue ファイルを変換 '^.+\\.[jt]sx?$': 'babel-jest', // JS/TS は Babel で処理 }, }; |
補足:
identity-obj-proxyは CSS モジュールのモックに特化したパッケージです。インストールし忘れるとmoduleNameMapperが解決できずテストが失敗します。
3. テストコードの基本構造
3‑1. テストスイートとケース (describe / test)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// src/math.js export const add = (a, b) => a + b; export const sub = (a, b) => a - b; // test/math.test.js import { add, sub } from '../src/math'; describe('Math ユーティリティ', () => { test('add は正しい合計を返す', () => { expect(add(2, 3)).toBe(5); }); it('sub は負の結果も正しく扱える', () => { expect(sub(1, 4)).toBe(-3); }); }); |
describeがテスト対象(モジュール・コンポーネント)ごとの スイート を表し、失敗時に階層化されたレポートが出力されます。testとitは同義で、個別の テストケース を定義します。
3‑2. アサーションとカスタムマッチャー
組み込みマッチャー例
|
1 2 3 |
expect([1, 2, 3]).toContain(2); expect({ name: 'Bob' }).toEqual({ name: 'Bob' }); |
カスタムマッチャーの作り方
src/__tests__/matchers.js
|
1 2 3 4 5 6 7 8 9 10 11 |
export const toBeValidEmail = (received) => { const pass = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(received); return { pass, message: () => pass ? `expected ${received} not to be a valid email` : `expected ${received} to be a valid email`, }; }; |
jest.setup.js(Jest のセットアップファイルに記載)
|
1 2 3 |
import { toBeValidEmail } from './src/__tests__/matchers'; expect.extend({ toBeValidEmail }); |
テスト側
|
1 2 3 4 |
test('メールアドレス形式のバリデーション', () => { expect('alice@example.com').toBeValidEmail(); }); |
ベストプラクティス:カスタムマッチャーはプロジェクト共通の検証ロジックを一元化し、テストコードの可読性と保守性を高めます。
4. モック機能の実践
4‑1. jest.mock の基本
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// src/api.js export const fetchUser = async (id) => { const res = await fetch(`https://api.example.com/users/${id}`); return res.json(); }; // test/api.test.js import { fetchUser } from '../src/api'; jest.mock('../src/api'); // 自動モック化 test('fetchUser が正しいデータを返す', async () => { fetchUser.mockResolvedValue({ id: 1, name: 'Alice' }); const data = await fetchUser(1); expect(data.name).toBe('Alice'); expect(fetchUser).toHaveBeenCalledWith(1); }); |
4‑2. 手動モック (__mocks__ ディレクトリ)
|
1 2 3 4 5 |
src/ ├─ utils.js └─ __mocks__/ └─ utils.js ← 手動モック本体 |
手動モック実装例(src/__mocks__/utils.js)
|
1 2 |
export const getRandomNumber = jest.fn(() => 42); |
テスト側
|
1 2 3 4 5 6 7 |
import { getRandomNumber } from '../src/utils'; // 自動で manual mock が適用される test('getRandomNumber は常に 42 を返す', () => { expect(getRandomNumber()).toBe(42); expect(getRandomNumber).toHaveBeenCalledTimes(1); }); |
注意点まとめ
| 項目 | 推奨対策 |
|---|---|
| モジュールキャッシュの汚染 | jest.resetModules() または個別テストで mockClear() を実行 |
| スコープ漏れ | 必要に応じて jest.isolateModules(() => { … }) で分離 |
| 手動モックと自動モックの併用 | 同一ファイルを同時に jest.mock と manual mock しない |
5. 非同期テストの書き方
5‑1. async/await を使うパターン(推奨)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// src/service.js export const getData = async (id) => { const res = await fetch(`https://api.example.com/data/${id}`); if (!res.ok) throw new Error('Network error'); return res.json(); }; // test/service.test.js import { getData } from '../src/service'; global.fetch = jest.fn(); test('getData が正しくデータを取得できる', async () => { fetch.mockResolvedValue({ ok: true, json: async () => ({ id, value: 'hello' }), }); const result = await getData(10); expect(result.value).toBe('hello'); }); |
- テスト関数が
asyncの場合、Jest が自動で Promise 完了 を待ちます。doneコールバックは不要です。
5‑2. Promise 直接返却と旧来の done
| 方法 | メリット | デメリット |
|---|---|---|
return promise |
簡潔、エラーが自動で失敗扱い | 非同期ロジックが長くなると見通しが悪くなる |
async/await (推奨) |
直感的なフロー、try/catch で例外処理が可能 | - |
done コールバック |
必要なケース(例: コールバックベースの API)に限定 | 途中例外で done が呼ばれずテストがタイムアウトしやすい |
例:Promise 返却
|
1 2 3 4 5 |
test('fetch の成功を Promise で検証', () => { fetch.mockResolvedValue({ ok: true, json: async () => ({}) }); return expect(fetch('/api')).resolves.toHaveProperty('ok', true); }); |
例:done(非推奨)
|
1 2 3 4 5 6 7 8 9 10 |
test('旧式の done 使用例', (done) => { fetch('/api') .then(res => res.json()) .then(data => { expect(data).toHaveProperty('id'); done(); }) .catch(err => done(err)); // エラー時も必ず呼ぶ }); |
ベストプラクティス
- 基本は async/await または Promise 返却 を選択。
- done はレガシーコードのリファクタリング対象とする。
- 長時間実行テストは jest.setTimeout(30000) 等でタイムアウトを明示的に拡張。
6. テスト自動化・運用ガイド
6‑1. カバレッジ取得とレポート設定
|
1 2 3 4 5 6 7 8 9 10 11 12 |
// jest.config.js(カバレッジ追加版) module.exports = { collectCoverage: true, coverageDirectory: '<rootDir>/coverage', collectCoverageFrom: [ 'src/**/*.js', '!src/index.js', // エントリポイントは除外例 '!src/**/constants.js', // 定数ファイルは除外例 ], coverageReporters: ['html', 'text-summary'], }; |
実行コマンド
|
1 2 3 |
npm test -- --coverage # または npx jest --coverage # => coverage/index.html が生成され、ブラウザで可視化できる |
6‑2. CI(GitHub Actions)への組み込み
.github/workflows/ci.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
name: CI on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest strategy: matrix: node-version: [20.x] # LTS バージョン推奨 steps: - uses: actions/checkout@v3 - name: Set up Node.js uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'npm' - run: npm ci # clean install - run: npx jest --ci --coverage |
--ciオプションは CI 環境での最適化(シリアル実行・キャッシュ削減)を有効にします。- テスト結果とカバレッジサマリが Pull Request のステータスチェックとして表示されます。
6‑3. ディレクトリ構成・命名規則
|
1 2 3 4 5 6 7 8 9 10 11 |
project/ ├─ src/ # アプリケーションコード │ └─ utils.js ├─ __tests__/ # テストコード(任意でも推奨) │ ├─ utils.test.js │ └─ __mocks__/ ├─ jest.config.js ├─ package.json └─ .vscode/ └─ launch.json # デバッグ設定 |
- テストファイル名は
*.test.jsまたは*.spec.jsを統一。 - モックは
__mocks__/配下に置くと自動的に適用される。
VSCode で Jest デバッグ
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// .vscode/launch.json { "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Jest: Run All Tests", "program": "${workspaceFolder}/node_modules/jest/bin/jest", "args": ["--runInBand"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" } ] } |
6‑4. よくあるエラーと対処法
| エラーメッセージ | 主な原因 | 解決策 |
|---|---|---|
Cannot find module |
Babel が対象拡張子を変換できていない、または moduleNameMapper が不足 |
transform に正規表現を追加、もしくはマッピング設定を見直す |
Exceeded timeout of 5000 ms |
非同期処理が完了しない、done 未呼び出し |
async/await に書き換えるか jest.setTimeout(10000) 等で延長 |
| モックが適用されない | jest.mock がインポートより後に記述されている |
テストファイルの先頭(インポート直前)に配置、または jest.isolateModules を使用 |
| カバレッジが 0% | collectCoverageFrom の除外パターンが過剰 |
必要なソースだけを列挙し、除外は最小限に抑える |
7. 参考文献
-
Jest公式ドキュメント – Getting Started
https://jestjs.io/docs/getting-started(2024 年版) -
Jest Configuration – Advanced Options
https://jestjs.io/docs/configuration(2024 年版) -
identity-obj-proxy – npm package
https://www.npmjs.com/package/identity-obj-proxy
次のステップ
- 本ガイドの手順で
npm testが成功することを確認。 - プロジェクトに CI(GitHub Actions)とカバレッジ設定を追加し、プルリクエストごとに自動テストが走る環境を構築。
- 必要に応じてカスタムマッチャーや手動モックを導入し、テストの表現力を高めましょう。
以上で Jest を使ったテスト基盤構築 の基本は完了です。実務で活用しながら、チームに最適なテストフローを磨いてください。