testing-patterns
テスト駆動開発(TDD)のワークフローとテスト戦略パターンに関する知識です。テストピラミッド、カバレッジ戦略、モッキング手法、アンチパターンなどを網羅しています。テストコードの作成、テスト戦略の設計、テストカバレッジのレビューを行う際に活用できます。
description の原文を見る
TDD workflow and test strategy patterns including test pyramid, coverage strategies, mocking approaches, and anti-patterns. Load when writing tests, designing test strategies, or reviewing test coverage.
SKILL.md 本文
テストパターン
効果的なテストに対する体系的なアプローチです。以下の場合に使用します:
- テストを作成または変更する場合(アンチパターンのリファレンスを読み込む)
- 新機能のテスト戦略を設計する場合
- テストカバレッジの妥当性をレビューする場合
- テストフレームワークまたはインフラストラクチャを実装する場合
テスト駆動開発(TDD)
新機能のコードに対して TDD は必須です。 実装前にテストを作成します。
TDD サイクル
┌─────────────────────────────────────────┐
│ │
│ 1. RED → Write failing test │
│ 2. GREEN → Minimal code to pass │
│ 3. REFACTOR → Clean up, tests stay green │
│ 4. REPEAT │
│ │
└─────────────────────────────────────────┘
TDD が必要な理由
| メリット | TDD による実現方法 |
|---|---|
| 過度なモッキングを防止 | モッキング前にテストが何を必要とするかが明確になる |
| テスト専用の本番コードが存在しない | 最小限の実装 = 余分なコードなし |
| 実際の動作をテストする | 失敗するテストは実際に何かをテストしている証拠 |
| 設計が向上 | テスト可能なコード = 疎結合なコード |
TDD の適用場面
| 状況 | TDD? | 備考 |
|---|---|---|
| 新機能 | ✅ 常に | コアワークフロー |
| 動作変更 | ✅ 常に | テストを先に変更してからコード変更 |
| バグ修正 | ✅ 推奨 | バグを再現するテストを先に書く |
| 純粋なリファクタリング | ⚠️ オプション | 既存テストでカバー可能 |
| 探索的スパイク | ❌ スキップ | ただし後で TDD で書き直す |
TDD 違反
実装がテストなしで提出された場合:
- 「TDD が必要」として却下
- どのテストが存在すべきかを指定
- 実装者がテストを先に書いてからコードを書く
詳細なアンチパターンとゲート機能については、references/testing-anti-patterns.md を参照してください。
テストピラミッド
/\
/ \ E2E テスト (10%)
/----\ 低速、コスト高、少数
/ \
/--------\ 統合テスト (20%)
/ \ 中速、焦点的
/------------\
/ \ ユニットテスト (70%)
/________________\ 高速、独立、多数
ユニットテスト
概要: 単一の関数またはクラスを独立してテストする 対象: すべてのビジネスロジック、ユーティリティ、データ変換 速度: ミリ秒 独立性: 外部の依存関係(DB、ネットワーク、ファイルシステム)をモック
# 良いユニットテスト
def test_calculate_discount():
order = Order(items=[Item(price=100)])
assert order.calculate_discount(0.2) == 80
# 悪い例:統合テストであってユニットテストではない
def test_order_discount():
db.create_order(...) # データベースにアクセス
api.apply_coupon(...) # 外部呼び出し
統合テスト
概要: コンポーネント間の相互作用をテストする 対象: データベースクエリ、API コントラクト、サービス境界 速度: 秒単位 独立性: テスト対象のコンポーネントについては実際の依存関係を使用
# 統合テスト:DB 相互作用をテスト
def test_user_repository_finds_by_email():
repo = UserRepository(test_db)
repo.create(User(email="test@example.com"))
found = repo.find_by_email("test@example.com")
assert found.email == "test@example.com"
E2E テスト
概要: 完全なユーザーワークフローをテストする 対象: クリティカルパス、スモークテスト、ハッピーパス 速度: 分単位 独立性: なし(完全なシステムをテスト)
# E2E テスト:完全なフローをテスト
def test_user_can_checkout():
browser.goto("/")
browser.login("user@example.com", "password")
browser.add_to_cart("product-1")
browser.checkout()
assert browser.has_text("Order confirmed")
カバレッジ戦略
カバーすべき項目(優先順)
- ビジネスロジック — 収益に影響する計算
- セキュリティ境界 — 認証、検証、アクセス制御
- エラーパス — 例外処理、エッジケース
- 統合ポイント — API コントラクト、DB クエリ
- ハッピーパス — 標準的なユーザーワークフロー
優先しないべき項目
- ロジックのないゲッター/セッター
- フレームワークコード(既にテスト済み)
- サードパーティライブラリ
- 一度限りのスクリプト
- UI レイアウト(重要な場合を除く)
カバレッジターゲット
| 種類 | ターゲット | 備考 |
|---|---|---|
| ユニット | 80% 以上 | カバレッジ数値ではなくロジックに焦点を当てる |
| 統合 | クリティカルパス | すべての組み合わせをテストしない |
| E2E | ハッピーパスのみ | 5~10 の主要シナリオ |
エッジケース生成
体系的なアプローチ
数値入力の場合:
- ゼロ
- 負の数
- 非常に大きな数(オーバーフロー)
- 浮動小数点精度
- 境界値(n-1, n, n+1)
文字列入力の場合:
- 空文字列
- 非常に長い文字列
- Unicode/絵文字
- 特殊文字
- 空白のみ
- SQL/HTML インジェクション攻撃試行
コレクションの場合:
- 空
- 単一要素
- 多数要素
- 重複
- null/undefined 要素
日付の場合:
- うるう年
- タイムゾーン境界
- 夏時間遷移
- 遠い過去/未来
- 無効な日付
例マトリックス
| 入力 | シナリオ | 期待される結果 |
|-------|----------|----------|
| price | 0 | 無料商品の処理 |
| price | -5 | 検証エラー |
| price | 999999.99 | 大きな数値の表示 |
| name | "" | 必須フィールドエラー |
| name | "a"*1000 | 切り詰めまたはエラー |
| email | "test" | 無効な形式エラー |
モッキングパターン
モック対象とすべき場面
| コンテキスト | 何をモック? | 理由 |
|---|---|---|
| ユニットテスト | 外部の依存関係(DB、ネットワーク、時刻) | 独立性と速度 |
| 統合テスト | 外部サービスのみ | 実際のコンポーネント相互作用をテスト |
| E2E テスト | なし | 実際のシステムをテスト |
⚠️ TDD は過度なモッキングを防止します:テストを先に書いて失敗させることで、正確に何をモックする必要があるかがわかります。
モック vs スタブ vs スパイ
# スタブ:定められた応答を返す
payment_gateway = Mock()
payment_gateway.charge.return_value = {"status": "success"}
# モック:相互作用を検証する
email_service = Mock()
order.complete()
email_service.send.assert_called_once_with(
to="user@example.com",
subject="Order Confirmed"
)
# スパイ:実装をラップする
real_logger = Logger()
spy_logger = Mock(wraps=real_logger)
# 実際のメソッドを呼び出しながら呼び出しを記録
モッキングの鉄則
- モックの動作をテストしない — 依存関係からユニットを隔離するためにモックを使用しますが、モックの存在ではなく、ユニットの動作をアサート(検証)します。アサーションが
expect(mockThing).toBeInTheDocument()の場合、コードではなくモックをテストしています。 - 理解せずモックしない — モック化する前に副作用を把握する
- 不完全なモックを作成しない — 実際の API 構造を完全にミラーする
アンチパターンの赤信号
- モック設定がテストロジックより長い
*-mockテスト ID に対するアサーション- モックが必要な理由を説明できない
- 「念のため」モック化する
詳細なアンチパターン:references/testing-anti-patterns.md
テスト品質チェックリスト
| 品質 | チェック項目 |
|---|---|
| 読みやすさ | 新人開発者が 30 秒で理解できるか? |
| 独立性 | 他のテストに依存せず失敗するか? |
| 速度 | ユニットテスト < 100ms、統合テスト < 5s? |
| 決定論的 | 毎回同じ結果か? |
| 焦点性 | 1 テストに 1 つのアサーション(または論理的グループ)か? |
| 保守性 | 間違った理由で壊れないか? |
テスト命名
test_[unit]_[scenario]_[expected]
test_calculateDiscount_withExpiredCoupon_returnsZero
test_userRepository_findByEmail_whenNotFound_returnsNone
test_checkout_withEmptyCart_showsError
フレームワーク固有のガイダンスについては、references/testing-frameworks.md を参照してください。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- ujo78
- ライセンス
- MIT
- 最終更新
- 2026/3/19
Source: https://github.com/ujo78/Minecraft-server-hosting-panel / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。