Slide 1

Slide 1 text

テスト駆動開発(TDD) 入門 動作する綺麗なコードへの実践的アプローチ 1

Slide 2

Slide 2 text

想定読者 プログラミング経験のある開発者 ▶ テスト自動化に興味がある方 ▶ コードの品質向上を目指している方 ▶ アジャイル開発手法を学びたい方 ▶ 2

Slide 3

Slide 3 text

目次 1. テスト駆動開発(TDD) の本質 2. テスト駆動開発のサイクル 3. 問題の分割方法 4. テストコード作成のコツ 5. 実装の3 つのアプローチ 6. テストコードのメンテナンス性 3

Slide 4

Slide 4 text

本スライドのゴール TDD の基本概念と実践方法の理解 ▶ テストファーストの考え方の習得 ▶ 実装パターンの使い分けの習得 ▶ 保守性の高いテストコード作成スキルの獲得 ▶ 4

Slide 5

Slide 5 text

用語 TDD: Test-Driven Development (テスト駆動開発) ▶ レッド: テストが失敗している状態 ▶ グリーン: テストが成功している状態 ▶ リファクタリング: 外部の振る舞いを変えずに内部構造を改善すること ▶ アサーション: テストにおける期待値の検証 ▶ DRY: Don't Repeat Yourself (重複を避けること) ▶ テストファースト: 実装前にテストを書く方法 ▶ モック: テスト用の代用オブジェクト ▶ カバレッジ: テストによるコードの網羅率 ▶ 5

Slide 6

Slide 6 text

1. テスト駆動開発(TDD) の本質 6

Slide 7

Slide 7 text

TDD とは Kent Beck 氏が考案した開発手法 ▶ 「動作する綺麗なコード」を目指す ▶ 通常のプログラマーが着実に前進するための技法 ▶ 7

Slide 8

Slide 8 text

TDD の特徴 天才プログラマーのためではない ▶ 常に設計し続ける(設計をしないという誤解がある) ▶ 分割統治アプローチを採用 ▶ 8

Slide 9

Slide 9 text

理想のコードへの2 段階アプローチ 1. まず「動作するコード」を作る たとえ汚くても動作優先 - スピード重視 - 2. その後「綺麗なコード」に改善 動作を保ったまま - リファクタリングによる品質向上 - 9

Slide 10

Slide 10 text

重要な考え方 一気に理想のコードは書けない ▶ 小さなステップを積み重ねる ▶ フィードバックを常に活用 ▶ テストを通じて設計を改善 ▶ 10

Slide 11

Slide 11 text

2. テスト駆動開発のサイクル 11

Slide 12

Slide 12 text

レッド・グリーン・リファクタリング 3 つのフェーズを繰り返す基本サイクル ▶ 各フェーズには明確な役割がある ▶ 7 つのステップで具体化される ▶ 12

Slide 13

Slide 13 text

開発の7 ステップ 1. 目標設定と簡単な設計 2. 次の目標選択 3. テストを書く(テストファースト) 4. テスト実行で失敗確認(レッド) 5. 実装を行う 6. テストの成功確認(グリーン) 7. リファクタリング 13

Slide 14

Slide 14 text

開発の7 ステップ 1. 目標設定と簡単な設計 現在の理解をテキストファイルに記録 ▶ TODO リストの作成(過剰気味に) ▶ 実装前に全体像を把握 ▶ 2. 次の目標選択 テスト容易性が高いものを優先 ▶ 重要度との両立を考慮 ▶ 14

Slide 15

Slide 15 text

開発の7 ステップ ( 続き) 3. テストを書く(テストファースト) 利用者視点でテスト作成 ▶ 実装前にテストを書く ▶ API の使いやすさを検証 ▶ 4. テスト実行で失敗確認(レッド) 意図的な失敗 ▶ 予想通りの失敗を確認 ▶ 15

Slide 16

Slide 16 text

開発の7 ステップ ( 続き) 5. 実装を行う テスト成功が最優先 ▶ 最短距離でグリーンを目指す ▶ コード品質は二の次 ▶ 6. テストの成功確認(グリーン) 全テストの成功を確認 ▶ 16

Slide 17

Slide 17 text

開発の7 ステップ ( 続き) 7. リファクタリング テスト成功を維持したまま改善 ▶ プロダクトコード/ テストコード両方が対象 ▶ 5-10 分程度で区切りをつける ▶ TODO リストに戻る ▶ 17

Slide 18

Slide 18 text

サイクルの特徴 各ステップが小さい ▶ フィードバックが早い ▶ 常に動作する状態を維持 ▶ 品質と速度のバランスを取る ▶ 設計が徐々に改善される ▶ 18

Slide 19

Slide 19 text

3. 問題の分割方法 19

Slide 20

Slide 20 text

テスト容易性と重要度による分類 2 つの軸で問題を整理 ▶ 優先順位付けの指針となる ▶ 実装の順序を決定する基準 ▶ 20

Slide 21

Slide 21 text

テスト容易性の3 要素 観測の容易さ (Observability) 制御の容易さ (Controllability) 十分な小ささ (Size) テストから結果が見えやすいか ▶ 出力が明確か ▶ 入力値の設定が容易か ▶ テスト実行の制御が可能か ▶ テストの範囲が適切か ▶ 一度に検証する機能が限定的か ▶ 21

Slide 22

Slide 22 text

I/O 処理の分離 プリント処理などのI/O 処理 ▶ テスト容易性が低い - 本質的なロジックから分離する - 変換処理として再定義 - 分離の利点 ▶ テストが書きやすくなる - ロジックが明確になる - 保守性が向上する - 22

Slide 23

Slide 23 text

正常系・準正常系の区別 正常系から着手 ▶ 基本的な機能を先に実装 - 具体的な値から始める - 例:1, 2 などの単純なケース - 徐々に抽象化 ▶ パターンを見出す - 一般化を進める - リファクタリングで改善 - 23

Slide 24

Slide 24 text

重複への対応 2 つのアプローチ 2 アウト派 ▶ 2 箇所の重複で即対応 - 早めの統合を重視 - リスク:早すぎる抽象化 - 3 アウト派 ▶ 3 箇所の重複まで待つ - パターンの確認を重視 - リスク:重複の放置 - チームで方針を統一することが重要 ▶ 24

Slide 25

Slide 25 text

4. テストコード作成のコツ 25

Slide 26

Slide 26 text

下から上への記述順序 1. 検証(Assert) から開始 ▶ テストのゴールを明確化 - 期待値を最初に定義 - テストの目的を明確に - 2. 実行(Act) を記述 ▶ テスト対象の処理を実行 - 必要最小限の操作 - 3. 準備(Arrange) を最後に ▶ 必要なデータを用意 - テストの前提条件を整備 - 26

Slide 27

Slide 27 text

1 テスト1 アサーションの原則 アサーションルーレットの問題点 例外的なケース テスト失敗時のデバッグが困難 ▶ テストの意図が不明確 ▶ 保守性の低下 ▶ End-to-End テスト ▶ 実行コストが高いテスト ▶ 論理的に関連する複数の検証 ▶ 27

Slide 28

Slide 28 text

テストメソッドの命名規則 基本方針 仕様レベルの言葉を使用 ▶ 日本語での記述を推奨 ▶ テストが動作するドキュメントに ▶ 28

Slide 29

Slide 29 text

重要な注意点 テスト間の独立性を保持 ▶ 副作用を避ける ▶ 可読性を維持 ▶ 29

Slide 30

Slide 30 text

5. 実装の3 つのアプローチ 30

Slide 31

Slide 31 text

1. 仮実装アプローチ (Fake It) 2. 三角測量アプローチ (Triangulation) 3. 明白な実装アプローチ (Obvious Implementation) 31

Slide 32

Slide 32 text

1. 仮実装アプローチ (Fake It) 特徴 メリット デメリット 最も単純な実装(例:return 1 ) ▶ テストを最短で通す ▶ 一時的な実装 ▶ テストコードの検証が可能 ▶ 設計に集中できる ▶ 素早くグリーンに到達 ▶ 後で書き直しが必要 ▶ 技術的負債になる可能性 ▶ 32

Slide 33

Slide 33 text

2. 三角測量アプローチ (Triangulation) 特徴 メリット デメリット 複数のテストケースを用意 ▶ 具体例を増やしながら実装を一般化 ▶ 段階的な実装の改善 ▶ 実装の方向性が不明確な場合に有効 ▶ 段階的に理解を深められる ▶ 過剰な一般化を避けられる ▶ 開発速度が遅くなる ▶ テストケースが増える ▶ 33

Slide 34

Slide 34 text

3. 明白な実装アプローチ (Obvious Implementation) 特徴 メリット デメリット 一気に本実装まで進める ▶ テストを書いてすぐに実装 ▶ 最短距離での実装 ▶ 最も効率的 ▶ 開発速度が速い ▶ 余分なステップを省ける ▶ 経験が必要 ▶ 見通しを誤る可能性 ▶ リスクが高い ▶ 34

Slide 35

Slide 35 text

アプローチの使い分け 状況に応じた選択 使い分けの例 問題の理解度 ▶ 技術的な確信度 ▶ 時間的な制約 ▶ 1. 新しい概念の実装 → 仮実装 2. 複雑なロジック → 三角測量 3. 既知のパターン → 明白な実装 35

Slide 36

Slide 36 text

6. テストコードのメンテナンス性 36

Slide 37

Slide 37 text

動作するドキュメントとしてのテスト 構造化の重要性 TODO リストの構造をテストコードに反映 ▶ ネストしたクラス構造の活用 ▶ 仕様の階層構造を表現 ▶ 37

Slide 38

Slide 38 text

メンテナンス性向上のための施策 重複の適切な除去 テストの独立性確保 共通セットアップコードの抽出 ▶ 不要なテストケースの削除 ▶ DRY 原則の適用 ▶ テスト順序への依存を避ける ▶ 並列実行可能な設計 ▶ 副作用の排除 ▶ 38

Slide 39

Slide 39 text

テストコード整理のタイミング ベストプラクティス 実装直後の整理 ▶ 意図が記憶に新しい - 仕様理解が明確 - 定期的なリファクタリング ▶ コードレビュー時 - 新機能追加時 - バグ修正時 - 39

Slide 40

Slide 40 text

長期的なメンテナンス考慮事項 チーム内での共有 継続的な改善 命名規則の統一 ▶ 構造化ルールの確立 ▶ レビュー基準の明確化 ▶ 定期的な見直し ▶ 不要コードの削除 ▶ テストカバレッジの維持 ▶ 40

Slide 41

Slide 41 text

まとめ TDD の3 つの重要なスキル 次のステップ 1. 問題の分割力 2. 実装アプローチの使い分け 3. テストコードの構造化能力 小さな問題での練習 ▶ チームでの実践 ▶ 継続的な改善 ▶ 41