概要
“テストを書いたコードは壊れない”のではない。
“壊れても気づける構造”に変わる。それがテスト戦略の本質である。
JavaScriptの開発において、ユニットテスト・統合テスト・E2Eテストは単なる実装補助ではなく、設計上の意思決定ツールである。
本稿では、テスト階層を設計上のスコープとして捉え、それぞれの目的・設計・選定技術・分離戦略を体系的に解説する。
1. テストピラミッドの設計原則
┌────────────┐
│ E2E │ ← 仕様検証・ブラウザ操作
├────────────┤
│ Integration│ ← ユニット同士の連携
├────────────┤
│ Unit │ ← 単機能ロジック検証
└────────────┘
- ✅ 単体で速いユニットテストを基盤に
- ✅ 統合で接続ミス・依存関係を検証
- ✅ E2Eで“ユーザー目線の仕様”を検証
2. ユニットテストの設計
✅ 目的:1つの関数・モジュールの振る舞いの検証
// sum.js
export function sum(a, b) {
return a + b;
}
// sum.test.js
import { sum } from './sum';
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
- ✅ 外部依存を持たない(副作用ゼロ)
- ✅ 仕様が関数レベルで明確になる
- ✅ フレームワーク例:Jest, Vitest
3. 統合テストの設計
✅ 目的:複数ユニット間の相互作用の検証
// userService.js
export async function getUserInfo(api, id) {
const res = await api.fetch(`/users/${id}`);
return res.data;
}
// userService.test.js
const mockApi = { fetch: jest.fn().mockResolvedValue({ data: { name: 'Toto' } }) };
test('getUserInfo returns user name', async () => {
const result = await getUserInfo(mockApi, 1);
expect(result.name).toBe('Toto');
});
- ✅ HTTP・DBなどの抽象化インターフェースをモック
- ✅ ユニットでは見えない依存間の連携不整合を検出
- ✅ モック戦略が設計の粒度に直結
4. E2Eテストの設計
✅ 目的:ブラウザ上でのユーザー操作とUIの仕様検証
// 使用例(Playwright / Cypress)
test('ログイン後にプロフィール画面が表示される', async ({ page }) => {
await page.goto('/login');
await page.fill('#email', 'toto@example.com');
await page.fill('#password', 'secret');
await page.click('text=ログイン');
await expect(page.locator('h1')).toHaveText('プロフィール');
});
- ✅ 実際のブラウザ + 本物のUI
- ✅ 自動UI回帰テストのコア
- ✅ 実行コストが高いため、重要機能に限定
5. カバレッジとテスト優先順位の判断軸
機能 | 変更頻度 | 重要度 | 優先すべきテスト |
---|---|---|---|
ロジック関数 | 高 | 中 | ユニット |
API通信 | 中 | 高 | 統合 |
認証周辺 | 低 | 高 | 統合 + E2E |
UI表示部 | 高 | 中 | スナップショット or E2E |
設定画面 | 低 | 低 | ユニット or 無し |
→ ✅ テストを書くコスト vs 発生しうる障害コストでバランスを設計
6. テストと設計の相関性
- 関数が長すぎる → テストが書きにくい → 設計が悪い
- 副作用が多すぎる → テストが不安定 → 責務が混在している
- 依存が密結合 → モックが困難 → 分離設計が必要
→ ✅ “テストしにくい構造”は、“壊れやすい構造”の警告である
よくあるミスと対策
❌ カバレッジ100%を目指すが意味のないテストばかり
→ ✅ “重要な振る舞い”に焦点を当てた設計的テストを優先
❌ UIテストをすべてE2Eで行い、実行が遅くなる
→ ✅ E2Eは仕様の証明、UI部品はユニット+スナップショットでカバー
❌ 外部APIに直で依存し、テストが不安定
→ ✅ 抽象化されたAPI層をインジェクション可能な形で分離
結語
テストとは、「壊れたことを検出する仕組み」ではない。
それは、「壊れても影響を限定できる構造」を作る設計の一部である。
- ユニットで責務を閉じ
- 統合で依存を検証し
- E2Eで体験を担保する
信頼性は偶然では得られない。
それは意図して設計された“検証構造”によってのみ生まれる。