Upgrade to Pro — share decks privately, control downloads, hide ads and more …

テスト駆動開発入門ハンズオン(後編)/TDD HandsOn 2

テスト駆動開発入門ハンズオン(後編)/TDD HandsOn 2

Hiroki Iseri

June 12, 2011
Tweet

More Decks by Hiroki Iseri

Other Decks in Programming

Transcript

  1. テストコードの運用 • TDDでのテストの持続的効果 – 単体テスト容易性の確保 – 実装仕様の確保(Test as Documantation) –

    自動化された回帰テスト環境の構築 • 継続運用の課題 – 単体テストとしての整理(前篇) – 運用環境の構築 – テストコードのメンテナンス
  2. 継続的インテグレーション(CI) • 自動化されたインテグレーションを継続的に実行 1. インテグレーションを統合・自動化 • 単体テストを統合 2. インテグレーションの実行を自動化 •

    インテグレーション用サーバを用意(Hudson有名) • 統合したインテグレーションを継続的に自動実行する – コミット時等。ナイトビルドよりもっと頻繁に – 継続的ですばやいフィードバックを実現
  3. 単体テスト運用環境 作業用PC 構成管理サーバ DB関連テスト ストレステスト 規格バリデーション コミット 更新・ 自動実行 更新・

    自動実行 更新・ 自動実行 制約のあるテストを分散運用することで SlowTest問題や高コストを回避する
  4. 実行の分散方針 • TDDのテストは軽快に • 「実行に0.1sもかかる単体テストは、遅い単体テストである」 (レガシーコード改善ガイド) • Slow Test問題:時間のかかるテストのせいでTDDの効率が落ちる •

    重い/制約のあるテストはサーバ側、自動化へ – 時間がかかる/特定環境依存/高コスト • TDDでは必要なテストのみ実行すればよい – 全テスト実行はサーバ側へ
  5. Fragile Test • 製品コードの変更に弱いテスト – 些細なコード変更で大量のテストが失敗 – 仕様や機能とは無関係な変更でテストが失敗 • リファクタリングやCover

    & Modifyのコストを 増大させる(TDDはリファクタリングを推進するはずなのに・・・) • TDDでのエントロピー問題
  6. Fragile Test void test_1() { Hoge hoge = new Hoge(…);

    … } void test_2() { Hoge hoge = new Hoge(…); … } void test_3() { Hoge hoge = new Hoge(…); … } …. void test_100() { Hoge hoge = new Hoge(…); … } void test_101() { Hoge hoge = new Hoge(…); … } void test_102() { Hoge hoge = new Hoge(…); … } テストメソッドがHogeクラスに過依存 Hogeクラスのコンストラクタが変更されたら大量のテスト失敗が発生
  7. 対策例:Creation Method public void testHoge_first() { Piyo piyo = new

    Piyo(1, 2, 3); ... } public void testHoge_second() { Piyo piyo = new Piyo(4, 5, 6); ... }
  8. Creation Method public void testHoge_first() { Piyo piyo = createUniquePiyo();

    ... } public void testHoge_second() { Piyo piyo = createUniquePiyo(); ... } public Piyo createUniquePiyo() { return new Piyo(generateValue(), generateValue(), generateValue()); } 「Customer」という製品コードのへの依存部が削減された
  9. テストの変更可能性に 基づいてテストを設計する • 単体テスト設計のアプローチ – 仕様ベース • 仕様分析によって、仕様保障を目的とするテストを設 計する –

    構造ベース • 構造分析によって、構造を網羅するようにテストを設 計する – 経験ベース • エラー推測、経験を元にテストを設計する
  10. アプローチの扱い int hoge(int input) { return input * 2; }

    Int型は仕様か、構造的な制約か
  11. 構造の変更可能性 • 構造ベースでも変更可能性に程度がある – 「仕様としての構造」 – 暫定実装 構造の変更可能性 大 小

    暫定実装 内部メンバ ライブラリ モジュール等 のインタフェース 規格化された構造
  12. 構造の変更可能性 • 構造の変更可能性の2軸 – 時間軸方向の変更可能性 – 構造軸方向の変更可能性 • Ex)時間軸方向の変更可能性 •

    短期(一時的) – 暫定使用、実装過程での仮実装など • 中期 – 機能、各部モジュールなど • 長期 – 公的規格、標準規格、根幹的なアーキテクチャなど
  13. アーキテクチャ設計による 変更可能性の管理 • TDDはアーキテクチャ設計で支える – 外部設計 • テスト容易性を阻害するDOCを局所化する – 内部設計

    • テスト容易性を確保する • TDDのインプットとなる各モジュールの仕様を定義する • 変更可能性対策をアーキテクチャに盛り込む • Ex)タイミング設計:単体テストを阻害しないように
  14. Cover & Modify • 手順 – 1 パスする回帰テストを確保する – 2

    テストが成功する状態を保ちつつ、コードを変 更する
  15. Cover & Modify例 • リファクタリング – 1 機能を保護するテストを確保する – 2

    テストがパスする状態を維持しながら、コード を変更する
  16. Cover & Modify例 • TDDによる機能変更 – 1 変更対象の回帰テストを書く – 2

    TDDのサイクルへ • 変更機能のテストを書く(Red) • 実装する(Green)
  17. TDDとCover & Modify • TDDとCover & Modifyは親和性が高い – コードをテストに対して最適化されるため、コード のテスト容易性が高まる

    • テストで保護しやすくなる – テストコードが確保される – そもそもCover & ModifyがTDDのようなもの
  18. コードの資産化まとめ • Edit & Pray • Cover & Modify –

    リファクタリング – 機能追加 • TDDのコード資産化効果
  19. テストコードのドキュメント化 • 「テストコード=実装仕様」という思想 – Test as Documantation – TDDでのテスト設計で目指される理想の1つ –

    テストコードを動く実装仕様書として活用する – バグ出し、品質保証ではなく、仕様記述という目 的でテスト設計を行う
  20. Example Driven Development • EDD。実例駆動開発。TDDの1種 • EDDでのテスト=テスト対象の使い方の実例 • テストファーストが苦手な人のためのプラク ティスとして有効

    • 手順: – 最初に実装コードの使い方の実例を考える – 実例をテストで表現する – 以後はTDDのサイクルへ
  21. BDDフレームワーク • JUnit – assertEquals("hoge name", hoge.getName()); – assertEquals(16, hoge.getAge());

    • JDave(BDDフレームワーク) – specify(hoge.getName(), must.equal("hoge name")); – specify(hoge.getAge(), must.equal(16));
  22. Characterization test(仕様化テスト) • Characterization testによるコード解析 – 1 適宜の入出力で解析対象のテストを書く(最 初は失敗させる) –

    2 テストがパスするまで入出力の値を調整する • テスト失敗したら期待値を実行値に置き換える • 例外が発生したら例外テストに置き換える – 目的が達成されるまでこれを繰り返し、テストを 継ぎ足していく
  23. レガシーコードでのTDD 1. (よく考える) 2. 依存性を排除する 1. 変更点を洗い出す 2. テストを書く場所を見つける 3.

    依存性を取り除く 3. テストを書く 4. テストをパスするコードを書く 5. リファクタリングする
  24. 依存性の排除 • リスクを許容するとしても、リスクを抑える – 低リスクなツール支援が使えるなら活用 – 低リスクな変更手段があるなら活用 • private→protected •

    finalを削除する – 上位のテストで補完 • リスクにある変更は慎重に考える – “よく考える” – スクラッチリファクタリングなどでイメージを固める
  25. 依存性の排除例 (コンストラクタのパラメータ化) public Hoge { private MissileCtrl missileCtrl; public Hoge()

    { missileCtrl = new MissileCtrl (): } public void piyo() { …. missileCtrl.発射(); …. missileCtrl.発射2(); …. } …. }
  26. 依存性の排除 (コンストラクタのパラメータ化) public Hoge { private MissileCtrl missileCtrl; public Hoge(MissileCtrl

    missileCtrl) { this.missileCtrl = missileCtrl; } public Hoge() { thils(new MissaileCtlr()); } public void piyo() { missileCtrl.発射(); } …. } Hoge(new FakeMissileCtrl());
  27. 最後のまとめ • 取りあえず覚えてもらいたい要点 – 継続的インテグレーション – Fragile Test – Cover

    & Modify – レガシーコードでのTDD – テストコード=ドキュメントとする考え方