Web Components に TDD の適用は難しい
Web Components は再利用性の高い UI コンポーネントを作るうえで非常に有用ですが、その内部構造や振る舞いが特殊であるため、TDD を直接適用しようとするといくつかの困難に直面します。
このドキュメントでは、その理由と、現実的かつ効果的なアプローチについて解説します。
Web Componentsを使う場合、TDDを「UIコンポーネント」そのものに厳密に適用しようとすると難易度が高くなるため、TDDは「アプリケーションのロジック層(ドメイン/ビジネスルール)」に対して適用するのが現実的かつ効果的です。
Web ComponentsとTDDを無理に結びつけず、「UIとロジックの責務分離」をして、ロジック側にTDDを適用するのが現実的で効果的です。 UI層は「接続部分を検証する程度」にとどめ、ロジックの堅牢性をTDDで担保するのがベストプラクティスです。
Web Components × TDD が難しい理由
なぜ Web Components に TDD をそのまま適用するのが難しいのか、具体的な技術的観点から見ていきましょう。
理由 | 詳細 |
---|---|
DOM操作と密接で状態が外部依存 | 属性・イベント・スロットなど、外部のHTMLやDOMとの相互作用が多い |
状態の変化が非同期 | 属性変更・イベントディスパッチ・描画の完了タイミングなどが async |
テストに Shadow DOM 対応が必要 | shadowRoot の中身まで確認しないと動作保証できない |
初期化やライフサイクルが特殊 | connectedCallback, attributeChangedCallback などを正確にテストするには環境が必要 |
よって、TDDに向いているのは「ドメインロジック層」
上記のような制約を踏まえると、TDD の効果を最大限に引き出すには、UI ではなくロジック層に注目するのが現実的です。
例:アプリケーション構造の分離
📁 /src
├── /components
│ └── my-user-form.ts ← Web Component(TDD困難な部分)
├── /domain
│ └── user.ts ← TDD対象(Userのバリデーション、登録条件など)
├── /services
│ └── user-service.ts ← TDD対象(登録処理、DB保存など)
└── /utils
└── email-validator.ts ← TDD対象(関数単位)
TDD対象例:
層 | テスト対象例 |
---|---|
domain/user.ts | User の生成条件、isValid() のようなドメインロジック |
services/user-service.ts | 登録・重複チェック・通知など、ビジネスルール |
utils/email-validator.ts | 単一関数としてのユニットテスト |
UI層(Web Components)は「テスト後」+「最小限の振る舞いテスト」でOK
UI 層もまったくテストしないわけではありませんが、TDD のような厳密なループで行うには適していません。
ここでは、UI に対する適切なテストアプローチについて説明します。
テストスタイル | 用途 |
---|---|
単体ユニットテスト | 属性・イベントの受け渡しなどシンプルな振る舞い確認 |
E2Eテスト(Playwrightなど) | ユーザー操作を通じた総合動作確認 |
Storybook + Jest | UI状態のスナップショットテスト(非推奨だが一部で使われる) |
責務を明確に分離する
そのためには、アプリケーション内の各層における責務の分離が不可欠です。
以下のように、ロジック・状態管理・UI を分けて考えると、それぞれに最適なテスト戦略が見えてきます。
対象層 | テスト戦略 |
---|---|
ビジネスロジック層 | 純粋なJavaScriptで実装し、TDDで徹底的にテスト |
状態管理層 | データの流れと状態変化をTDDでテスト |
プレゼンテーション層 | WebComponentsの見た目や振る舞いは、必要に応じて統合テストやビジュアルテストで補完 |
実際の開発フロー
では、実際にどのような順序で開発・テストを進めるのが効果的なのでしょうか。
TDD を活かした現実的なフローの一例を紹介します。
- ロジックやルールを TDD で先に設計・実装
- その後 Web Component の UI に「ロジック層を注入」
- UI 層では「最低限の振る舞い」だけをテスト(属性変化やイベント発火)