太田です。
私もつい最近,石井さんに紹介いただいたWeb上のドラフト版をようやく読み終わ
りました。日本語版楽しみですね。
以下,部署用にまとめたメモですが,よろしかったらご覧ください。
#長いのと私見(例題解説なし,パターンより)が入っていますがご容赦を。
TDD(Test Driven Development, テスト駆動型開発)調査メモ
1. TDDとは
Kent Beckが提唱している手法で,主にコーディングと単体テストで使用する手法で
す。以下のような流れでテストとコードを作成します。
1. テストを追加する
2. 既に作成済みのテストと新しいテストを合わせたすべてのテストを実行し,新し
いテストが失敗するのを確認する
3. テストを成功させるための最小限の修正を加える
4. 既に作成済みのテストを合わせたすべてのテストを実行する。すべてのテストが
成功するのを確認する
5. 実装コードからテストコードとの重複部分を取り除く(リファクタリング)
コーディング前にすべての設計を終えるのではなく,テストを作成しながら,コード
を洗練し,徐々に設計とコードの質を高めていきます。
(かなり誇張していますが)従来の設計,コーディング,テストの流れは,
規律がゆるい場合
1. IPO,流れ図,クラス図,シーケンス図などで設計する (実際に動くかどうかも分
からないに詳細に記述しろといわれても・・・)
2. 設計に従ってコーディングする (う,やっぱりこの設計駄目だよ。直しちゃえ。
ふふ,俺様のクールでエレガントなコードでカバーさ)
3. 適当な単体テストを作って,実行してみる (とはいえ,怖いので正常ケースだけ
にしておこう。まあ,実行していないパスもあるみたいだけど通ればいいや,納期も
迫っているしね)
4. 統合テスト,システムテストを実行する。バグが多量発生 (ああ,やっぱり)
5. 手当たり次第にデバッグ (何テストしたっけ,設計がまずかったから,設計書と
もコードはほとんど別物だし,もう発狂しそう)
6. 修正して結合してみると,別のところでバグ発生 (単体テストを残していないの
で影響範囲が分からん)
規律が厳しい場合
1. 設計と同時にテスト仕様書を作成する (こんなぎちぎちに決めちゃって,後から
設計が変わったらこのテスト項目はどうなるんでしょ)
2. 設計に従ってコーディングする (う,この設計もまずい。しかし,規則だからこ
の設計どおりに実装と・・・)
3. 単体テストの作成,実行する (カバレッジを満たすの面倒くさ〜い。後で設計に
修正が入って無駄になりそうなのにね)
4. 統合,システムテスト実行時に設計のまずさが露見 (ほらやっぱり)
5. 設計書,テスト仕様書書き直し (前のテストの意味は一体。書き直すのも自分
か,モチベーション下がりまくり,も〜いや)
というような悪循環に陥っていました。
Kent Beckがこのような悪循環を断ち切るために悩んだ末,たどり着いたのがTDDで
す。
2. TDDの対象
TDDの対象は最近のソフトウェア開発に代表される
・短納期
・複雑な技術
・不十分なスキル
・極度のプレッシャー
におかれている開発者(とそれと取り囲む人々)です。
3. TDDの心
Kent Beckは初めから完璧な設計,優れた設計などは出来ないことを長年にわたる開
発経験から理解しました。人は初めからすべてを見渡せるわけではない。すなわち初
めから優れた設計など出来るわけではない。学びながら,良い設計に近づいていける
のみだということが分かりました。
そしてたどり着いたのが,今,自分が分かっていることを明らかしに,それだけを実
装する,それ以上は別のことが分かるまで実装しないというTDDのアプローチである
といえます。
4. TDDの特徴
・実装コードを書く前にテストコードを書く
というのが一番なのですが,他にも以下の特徴があります。
・テストコードの量と実装コード量が同量くらいになる
・テストコードで設計仕様を表せる
・どの時点でも必ず,実装コードのすべてのステートメントカバレッジを満たすテス
トコードが存在する
・各テストケースはシンプルかつ具体的で(原則的に)実装コードと同じ言語で記述す
る
・初めから複雑な設計をしない
・完全な設計は存在せず,現時点で動作可能な最良な設計のみが存在する
・設計を洗練させる過程で始めて抽象化をする
・抽象化の知識として,リファクタリング,デザイン・パターンを使う
・TDDを支えるテスティングのためのパターンが存在する
・シンプルな設計になるため,コミュニケーションをするのにもUMLに代表される図
はほとんど不要。2重メンテはない (Kent Beck自身がほとんど図を使わないのも影響
しているかもしれません)
なお,TDDはXPのような開発全体を包括する方法論ではありませんので,どのような
方法論と組み合わせてもかまいません。XP,RUP,果てはウォータフォールの方法論
であっても,コーディングと単体テストの段階であれば組み合わせて使用できます。
4. TDDを実践すると
・設計が非常にシンプルになる
・複雑な条件判断,多重ループなどテストが困難なコードは書かなくなる
・自動的にステートメントカバレッジを満たす
・デバッグで苦しまなくなる
・安心してコードを書けるようになる
・テストを書くのが楽しくなってくる
という効果があります。TDDではテストが不可能なコードは書きませんからね。おの
ずと設計はシンプルかつ分かりやすくなります。テストを書くのがクリエイティブな
作業であるということも分かってきます。初めから完璧な設計を目指さなくて良いの
で,設計もコーディングも楽しくなります。テストを通過したコードしか書いていな
いし,いつでもすべての再テストが実行可能なので,デバッグにおびえることなく
コーディングが出来ます。
5. TDDの実際
これはここで説明するよりもKent Beckの実際の書籍を読んでいただくほうが分かり
やすいです。
Test-Driven Development: By Example (The Addison-Wesley Signature Series)
http://www.amazon.co.jp/exec/obidos/ASIN/0321146530/ref=ase_xpjp-22/250-6476057-7275420
の
Section I: Money Example
において,実際の開発におけるTDDの適応方法をやさしく解説しています。英語です
が非常に分かりやすい内容なのでぜひご一読ください。テストを書きながら,徐々に
設計とコードを洗練していくのを見ると,このアプローチがいかに優れたものか分か
ると思います。
ドラフト版でもよいならば
http://groups.yahoo.com/group/testdrivendevelopment/files/TDD17Jul2002.pdf
にて全原稿を読めます。
6. TDDを支えるパターン
TDDのプロセス自体は「1.」で書いたように非常に簡単ですぐにでも実施できるもの
ですが,実際にTDDを実践していくにあたってはもう少し指標が欲しいですね。それ
を支えるのが,TDDのためのパターンです。TDDのパターンは基本的なパターンが多い
ので,もう少し従来のテスト技法を活用した詳細なテストを実施したい場合は,Bob
BinderのTest Design Patterns(Testing Object-Oriented Systems: Models,
Patterns, and Tools (Addison Wesley Object Technology Series,
http://www.amazon.co.jp/exec/obidos/ASIN/0201809389/qid=1043908391/sr=1-10/ref=sr_1_2_10/250-6476057-7275420)
が役立ちます。
TDDのパターンとカテゴリは以下のようになっています。
・Test-Driven Development Patterns
・Red Bar Patterns
・Testing Patterns
・Green Bar Patterns
・xUnit Patterns
・Design Patterns
各パターンについて簡単に説明します。
6.1. Test-Driven Development Patterns
TDDの成功させるための基本的なパターンです。以下のパターンがあります。
・Test n.
テストは自動化し,常に実施しましょうというパターンです。
・Isolated Test
個々のテストケースは互いに依存関係がない粒度に分離しましょうというパターンで
す。
・Test List
まずテストする際に自分が知っていること,しようとしていることをテスト項目とし
て一通り列挙しましょうというパターンです。必要であれば新たに付け足します。
・Test First
コードの前にテストを先に書きましょうというパターンです。
・Assert First
テストケースに何を書いたら分からないときの指標となるパターンです。一度にたく
さんのことは考えられませんので,まず得たい出力だけ考えて,それをアサーション
で評価するテストコードを書きましょうというパターンです。
・Test Data
テストデータを選ぶときに指標となるパターンです。重複データを避ける,現実に使
用する値を選ぶなどです。
・Evident Data
テストデータには具体的な値を記述しましょうというパターンです。テストコードは
コンピュータのためではなく人のために書くものですので,冗長であっても人に分か
りやすい具体的な値を使いましょうというパターンです。テストコードにおいては定
数などを定義して使いまわすのは避けるべきです。
6.2. Red Bar Patterns
テストを追加するときのパターンです。xUnitで新しくテストを追加し,テストを失
敗させたときに表示される赤色のバーからきているようです。
・One Step Test
テストを一つ追加(リストがある場合,実装)していきましょうというパターンです。
・Starter Test
クラスを新しく作成する場合,最初のテストコードには生成して出力を得るだけのよ
うな簡単なものから始めましょうというパターンです。
・Explanation Test
オブジェクトの協調関係などが複雑な場合,説明のために実際にテストを作成し,実
行してみましょうというパターンです。
・Learning Test
言語やサードパーティーのAPIやクラスの機能について知りたい場合,そのAPIやクラ
スの振る舞いを見るテストを書いてみましょうというパターンです。
・Another Test
その他のテスト技法について考えたり,勉強したり,議論してみましょうというパ
ターンです。
・Regression Test
新しいテストを実行する際には同時に既存のすべてのテストケースも再実行(リグ
レッションテスト)しましょうというパターンです。
・Break
疲れたら休みましょうというパターンです。一息つくと新しいテスト項目が見つかっ
たり,打開策が見つかったりします。
・Do Over
テストとリファクタリングを繰り返す際のモチベーションを保つためのパターンで
す。気分の切り替え,ペアプログラミングなどです。
・Cheap Desk, Nice Chair
TDDを実践するのに必要な環境のパターンです。ペアプログラミングをする際には良
い椅子を用意しておきましょう。
6.3. Testing Patterns
実際にテストコードを書く際に有効なパターンです。
・Child Test
成功するテストコードをいくら増やしても切りがありません。思いつきでも良いので
失敗するテストコードを書いてみましょうというパターンです。
・Mock Object
データベースやネットワークなど単体テストで使用するには設定が困難なリソースが
あります。この場合,見かけ上はこれらのリソースに見えるハリボテのオブジェクト
(スタブ)を作成して,代用しましょうというパターンです。テストのために必要な最
低限度の機能だけを実装しましょう。
・Self Shunt
ObserverパターンにおけるSubjectのようなクラスをテストする際にテスト用の
Observerを作成するのではなく,テストコード自体をObserverにしてしまいましょ
う,ダミーのオブジェクトの作成する代わりにテストコード自体にダミーオブジェク
トの役割を持たせてしまいましょうというパターンです。
・Log String
テストコードが正しく流れているかを確認するためにシーケンスのログを取る文字列
を使いましょうというパターンです。
・Crash Test Dummy
あるメソッドAから別のメソッドBをよび,そのメソッドBが例外を起こしたときにメ
ソッドAが取る振る舞いをテストしたいような場合,その状況をテストコード内のテ
ストデータだけで起こすのは難しい場合があります。この場合,それらのメソッドを
備えるクラスを継承し,メソッドBで強制的に例外を発生させるようなクラスを作成
すればよいというパターンです。Javaであれば,メソッド内で内部クラスが使用でき
るため,例外を起こすダミークラスを別ファイルで作成する必要がありません。
・Broken Test
一人で開発している際にプログラミングを中断するときには最後の失敗する最後のテ
ストを残しておきましょうというパターンです。失敗するテストはプログラミングを
する際にどこから再開すればよいのか,中断した時点で何を考えていたのかを教えて
くれます。
・Clean Check-in
チームで開発する際にプログラミングを中断するときはすべてのテストを成功させて
から中断しましょうというパターンです。チームの場合,個人と違ってすべてのコー
ドを把握できるわけではないので,すべてのテストは成功しているという確信ある状
態で再開しなければならないためです。
6.4. Green Bar Patterns
失敗しているテスト(xUnitで赤いバーになっているテスト)を成功させるためのパ
ターンです。
・Fake It (‘Til You Make It)
とりあえず,成功するための最低限のコードを実装し,徐々に抽象化していきましょ
うというパターンです。
・Triangulate
実装コードを抽象化するために,テストケースを追加しましょうというパターンで
す。追加するのは重複するテストケースではなく,別の視点からのテストケースで
す。
・Obvious Implementation
実装するクラスがそれほど難しくない場合は”Fake It”,”Triangulate”のパター
ンを踏む必要はなく,テストを書いたら,そのままコードを書いても良いというパ
ターンです。
・One to Many
メソッドの処理対象(引数)のオブジェクトを単数から複数に変更する場合のテスト
コードと実装コードの変更手順を示すパターンです。一度に変更を実施しないで,途
中,単数と複数のオブジェクトをともに引数として取るようなテストとコードを実装
し,成功させた上で,複数オブジェクトのバージョンに移行します。
6.5. xUnit Patterns
JUnitに代表されるxUnit特有のパターンです。
・Assertion
アサーション(表明)の使用法に関するパターンです。例としてはオブジェクトの状態
を比較する際には内部実装に依存した形ではなく,メソッドの出力と比較するように
というものがあります。
・Fixture
テストケースに共通の処理は一つの場所にまとめましょうというパターンです。
xUnitではテスト開始前の共通処理としてはsetUpという名前のメソッド(もしくは関
数)が担当する場合が多いです。
・External Fixture
テスト開始前の共通処理で外部リソース(ファイル,ネットワーク,データベース)な
どを使用する場合,テスト終了後の共通処理もまとめましょうというパターンです。
xUnit ではtearDownという名前のメソッド(もしくは関数)が担当する場合が多いで
す。
・Test Method
テストケースをどのようにまとめるべきかの指標となるパターンです。一つのシナリ
オはメソッドにまとめ,共通のFixtureを使うテストメソッドは一つのクラスにまと
め,Fixtureが別の場合別々のクラスにする場合が多いようです。
・Exception Test
メソッドが例外を発生させる場合のテストコードを記述するためのパターンです。
JUnit場合,テストシナリオのコードを”try”と”catch”で囲み,テストシナリオ
の本来到達しない場所に”fail()”を記述します。
・AllTests
各テストクラスをまとめて実行するための方法を解説したパターンです。
6.6. Design Patterns
TDDでテストやリファクタリングをする際に有用なデザイン・パターンです。こちら
はTDDに限定されたパターンではありませんので,特に有用だと思ったもの以外は解
説いたしません。詳細は別途書籍などをご覧ください。
・Command
・Value Object
一度生成したら値の変更がない値取得専用のオブジェクトを作成するパターンです。
頭痛の種となるAlias(別名)問題を回避します。
・Null Object
頻繁にnullチェックをするコードを分かりやすくするためにnullをあらわすオブジェ
クトを作成し,そのオブジェクトにnull時の振る舞いを委譲するパターンです。クラ
スの利用側のコードがすっきりして分かりやすくなります。Nullポインタの問題にも
悩まされなくなります。
・Template Method
・Pluggable Object
・Pluggable Selector
リフレクションを使用してメソッドを一つしか持たない多数のサブクラスを作成する
のを防ぐパターンです。
・Factory Method
・Imposter
リファクタリングの過程で発生する名前だけが異なるコピー&ペーストなテストとク
ラスを作成するパターンです。このテストとクラスを作成している場合,リファクタ
リングの前兆です。
・Composite
・Collection Parameter
パラメータを集めるオブジェクトを作成し,作業対象のオブジェクトに渡していくパ
ターンです。連続した出力結果を得たい場合などに有効です。
・Singleton
7. リファクタリング
TDDではテストコードを記述しながら,徐々に実装コードを洗練していきます。この
過程でリファクタリングの技法を使います。TDDで用いるリファクタリングは以下の
とおりです。各リファクタリングの詳細については別途書籍(リファクタリング―プ
ログラムの体質改善テクニック Object Technology Series,
http://www.amazon.co.jp/exec/obidos/ASIN/4894712288/ref=sr_aps_b_/250-6476057-7275420)
などを参照してください。
・Reconcile Differences
・Isolate Change
・Migrate Data
・Extract Method
・Inline Method
・Extract Interface
・Move Method
・Method Object
・Add Parameter
・Method Parameter to Constructor Parameter
8. TDDをマスターする
TDDに対する疑問と回答です。重要だと思ったもののみ回答の概要を書いておきま
す。詳細は書籍をご覧ください。
・テストはどれほどの頻度,大きさで進めればよいですか?
・どの部分をテストすればよいですか?
条件,ループ,演算,ポリモルフィズム,そして書いたものすべて。仕様がある場合
それらも。
・テストケースが良いか悪いかの判断基準は?
悪いほうが簡単です。
・初期設定のためのコードが長い
・初期設定が重複している
・実行に時間がかかりすぎる
・別のテストケースに影響を与える
・TDDはフレームワークになりえますか?(英文の解釈間違えの可能性あり)
・テストケースの数はどれくらいあればよいですか?
単体テストに限って言えば,開発者の経験と考察の深さに依存します。
Kent Beckの場合,MTBF(平均故障間隔)を考えます。MTBFに影響を与えるような数値
は意味があるのかないのか考えてテスト項目にします。意味がないと考えるならテス
ト項目にはしません。
・冗長だと思ったテストケースはいつ削除すべきですか?
・TDDを実践するのにプログラミング言語は影響を与えますか?
・TDDは大規模システムにも使用できますか?
Kent Beckは40人/年,250,000行の実装コードと250,000行のテストコード,4,000個
のテストケースを持つプロジェクトにTDDを適応しました。20分で4,000個のテスト
ケースが終了します。日に何度も再実行していました。
・TDDはアプリケーションレベルのテスト(システムテスト)でも実施できますか?
これは難しいかもしれませんが,戦略レベル(大局的)には適応可能かもしれません。
・既にソースコードが存在するのですが,どうしたらTDDに移行できますか?
・TDDは誰のためのものなのですか?
開発者だけでなく,みんながハッピーになりますよ。
・TDDを開始するのは難しそうなのですが?
・TDDはパターンと関係がありますか?
・TDDは何故うまくいくのですか?
定量的に優れていると実証できているわけではありません。TDDの成功はむしろ心理
的なものです。
・何故TDDという名前にしたのですか?
・TDDはXP(Extreme Programming)のプラクティスとどのような関係がありますか。
XPでTDDを実施すれば,更にXPが強力に,逆も然りです。特に以下のプラクティスが
関係あります。
・ペア
・明るく仕事
・継続的な統合
・シンプルなデザイン
・リファクタリング
・いつでも出荷可能
・TDDでは無理なことは?
Kent Beck自身は正しいとも間違っているとも判断がつきかねるのですが,以下の問
題が指摘されています。
・GUIのテストを自動化できない (Swing, CGI, JSP/Servlet/Struts)
・分散オブジェクトは単体ではテストできない (RPC, CORBA, EJB)
・データベーススキーマの開発ではTest Firstは実現できない
・外部のツールによって自動生成されたコードはテストするはない
・BNFで記述するコンパイラやインタプリタにもTest Firstは使用できない
おおた oota_ken@....com
_________________________________________________________________
ネットを使うひとに有利な特典いっぱい MSN カード http://card.msn.co.jp/