2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptにおけるテスト戦略:ユニット・統合・E2Eの設計分離、依存の隔離とテストしやすい構造設計

Posted at

概要

テストとは「動くかの確認」ではない。
それは**“コードが意図通りであることを検証し、将来の変更で壊れないことを保証する構造的契約”**である。

特にJavaScriptのように動的で柔軟な言語では、設計上の一貫性と構造の保証がテストによって初めて成立する

本稿では、ユニット / 統合 / E2E テストの責務分離、依存隔離の技法、構造をテストしやすく保つための設計戦略を解説する。


1. テストの3レイヤーを明確に分ける

レイヤー 目的 テスト対象
ユニット 最小単位の動作検証 純粋関数 / 小ロジック
統合 複数モジュール間の整合性確認 API + 状態 + ロジック
E2E ユーザー視点での最終結果検証 ブラウザ + 操作
  • ✅ 各レイヤーで責務を明確に → 役割を混在させない設計が必要

2. テストしやすい構造の原則

// NG: 副作用を含む
function fetchUserAndSet() {
  const res = await fetch('/api');
  store.set(res.data);
}

// OK: 分離された構造
function parseUser(raw: any): User {
  return { id: raw.id, name: raw.name };
}
  • ✅ テストしたい処理は副作用から分離
  • ✅ 状態更新・I/O は ユースケースやラッパー関数に限定

3. 依存を注入可能に設計する(DI構造)

const fetchUserUseCase = (api: UserApi) => async (id: string) => {
  const user = await api.get(id);
  return user.name;
};
  • ✅ テストでは api をモックに差し替えるだけ → テスト容易性の担保
  • ✅ 本番では realApi を注入 → 切り替え設計が柔軟

4. テスト対象を「構造単位」で分離する

❌ 1つの関数でデータ取得 → 変換 → 状態更新 → UI表示まで全て実行  
✅ データ取得 → パース → 状態更新 を分割し、各関数をテスト可能に
// e.g.
function transformUser(data: ApiResponse): User { ... }
  • ✅ 関数粒度の適切な分割により、各関数をユニットテスト可能に

5. テスト構造のディレクトリ戦略

src/
├─ usecases/
│   └─ loginUser.ts
├─ __tests__/
│   └─ usecases/
│       └─ loginUser.test.ts
  • ✅ ファイル単位でペアにすることで 変更とテストの関係を明示
  • ✅ コンポーネントごと or ユースケースごとに粒度を合わせる

6. ユースケース単位でテストを書く意義

// test: loginUser
it('ログイン成功時にセッションが保存される', async () => {
  const deps = { authApi: mockApi, sessionStore: memoryStore };
  const result = await loginUser(mockInput, deps);

  expect(result.ok).toBe(true);
  expect(memoryStore.set).toHaveBeenCalledWith(expect.anything());
});
  • ✅ 「成功パス」+「失敗パス」+「例外パス」を明示的に網羅
  • ✅ UIやAPIが変わっても ユースケースの設計と保証は維持できる

設計判断フロー

① この処理は分離できるか? → 副作用からピュアな関数に切り出す

② 依存は注入可能になっているか? → モックを差し替えられるか?

③ ユースケース単位で振る舞いをテストできているか?

④ テストレイヤーが混在していないか? → ユニット / 統合 / E2E を明確に

⑤ 結果の検証だけでなく、状態変化や呼び出しも追えているか?

よくあるミスと対策

❌ UIコンポーネント内で fetch → set → render 全部を直接書く

→ ✅ ユースケースにロジックを切り出し、テスト対象を構造的に抽出


❌ モック注入ができず、テストで毎回APIを叩いてしまう

→ ✅ 依存注入(DI)で柔軟に差し替え可能な設計へ


❌ 1テストで全体を検証しようとしてE2Eが巨大化

→ ✅ 単体テスト / 統合テスト / E2E を明確に分離し、役割を限定


結語

テストとは「動作確認」ではない。
それは**“構造の設計を検証し、変更に耐える保証を作るための安全戦略”**である。

  • 責務ごとにテストレイヤーを分離し
  • 副作用を避けて構造を分割し
  • 依存注入でテスト性を確保し
  • ユースケース単位でアプリの動作を保証する

JavaScriptにおけるテスト戦略とは、
“コードの信頼性と未来への自由を両立するための構造的設計”である。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?