TDD とは?

2009/06/29 biac
2010/02/01 「3. TDD 三原則」 を追加

  1. Test-Driven Development
  2. TDD の概要
  3. TDD 三原則
  4. TDD の位置付け
  5. TDD のメリット
  6. TDD の効果
  7. TDD の前提となるスキル

1. Test-Driven Development

TDD とは、 ソフトウェア開発では Test-Driven Development のことです。
プログラムの仕様からソースコードを創り出す手順を規定しています。 具体的には、 テストファーストとリファクタリングを組み合わせた手法です。 日本語では、 一般的に 「テスト駆動開発」 と訳しています。

なお、 検索をするときに "TDD" だけだと、 通信技術の "Time Division Duplex" (時分割複信) や、 仮想の強襲揚陸潜水艦の名前 "Tuatha De Danann" が引っ掛かってきますので、 "テスト" とか "プログラム" といった単語も合わせて検索するとよいです。

2. TDD の概要

次の 3ステップを、 小刻みに、 かつ、 リズミカルに繰り返します。

  1. [Red] 失敗するテストを書く。 テストは自動実行できること。
  2. [Green] テストが成功するように、 製品ソースを書く。
  3. [Refactor] 製品ソースをリファクタリングし、 もう一度テストを実行する。

1. と 2. の部分は 「テストファースト」 と呼ばれます。 実際の作業では、 1. と 2. を繰り返し (テストファースト)、 一息ついたところでリファクタリングする、 という流れになります。
各ステップを端的に表す英単語は、 次のような意味です。

  1. [Red] = テストツール NUnit は、 失敗したテストを赤色で表示するから。 これはステップ 1. のゴール状態を表しています。
  2. [Green] = NUnit は、 成功したテストを緑色で表示するから。 これはステップ 2. のゴール状態を表しています。
  3. [Refactor] = 文字通りリファクタリングをするから。

この 3ステップでは、 アタマを切り替えます。 「その帽子を被る」 というような言い方をすることがあります。

  1. [Red] 「どんなものを作るか?」 厳密に設計しよう。
  2. [Green] 「どうやって作るか?」 テストに通りさえすれば良いのだ。
  3. [Refactor] 「これで良いか?」 未来のために、コードを綺麗に直しておこう。

※ なお、 「4. Check-in」 を付け加える場合もあります。

TDD は、 アジャイル開発手法から生まれてきた方法なので、 アジャイル開発の 「不要なドキュメントなら作らない」 という原則にしたがって、 短期記憶だけで済む程度に各ステップに掛ける時間を短くすることが推奨されます。 ( 短期記憶でまかなえなくなったら、 ドキュメントを作らざるをえません。 )

また、 TDD では、 後になってバグが見つかった場合、 テストが不足していたか間違っていたと考え、 テストを追加・修正してからバグフィックスします。 すなわち、 デバッグ時にも、 上記の 3ステップを繰り返します。

3. TDD 三原則

TDD、 というよりもテストファーストで製品コードを 「育て」 ていく時の指針があります。

  1. 失敗するユニットテストを成功させるためにしか、 プロダクトコードを書いてはならない。
  2. 失敗させるためにしか、 ユニットテストを書いてはならない。 コンパイルエラーは失敗に数える。
  3. ユニットテストを1つだけ成功させる以上に、 プロダクトコードを書いてはならない。

※ TDD 三原則の出自については、 「[ブログ紹介] TDD三原則」 をご覧ください。

三原則の 1. は、 テストファーストの原則を言っています。 先に、 失敗するテストケースを書き、 それに通るように製品コードを書くのだということです。
次の 2. は、 成功すると分かっているテストケースを書くことは無駄だから、 書かないように、 と言っています。 無駄なだけでなく、 仕様変更時に修正しなければならないテストコードも増えてしまいますから、 そんな負の資産は持つな、 というわけです。
最後の 3. は、 テストを通す以上の製品コードを書くなということです。 言い換えれば、 テストしていない製品コードがあってはならない、 という意味です。

なお、 提唱者の Robert C Martin 氏自身が言っておられるように、 実際の開発現場では例外はあります。 それは TDD から外れるということですが、 TDD をすることが目的ではなくて、 良いソフトウェアを早く完成させることが本当の目的であることを思い出してください。
私見ですが、 2. に関しては、 不安を解消するためなら (成功するはずの) テストケースを追加することはやったほうが良いと思っています。 また、 完全に TDD からは外れますが、 クラスライブラリーを作ったときなどに、 使い方を解説するためのテストケースをドキュメントとして後から追加することは良くあります。

4. TDD の位置付け

ソフトウェアの開発プロセスにおいて、 TDD は次のように位置付けられます。
× 開発プロセス全体はカバーしていない。
× 要求開発から、 プログラムレベルの外部設計までは、 カバーしない。
○ プログラムの下位レベル ( クラス、 メソッド ) の外部設計からテストまでを、 カバーする。
× プログラムレベルのテスト ( 通常、 GUI があるため TDD が困難 ) から、 ユーザーテスト ( 実際にユーザーに使って貰わねばテストにならない ) までは、 カバーしない。

以上は、 現在のところ、 という前提条件付きでの筆者の見解です。 自動実行できる形で要求を表現しテストできるようになれば、 開発プロセスのほぼ全体を TDD でカバーできるようになるかもしれません。

また、 TDD の適用範囲は、 実施するためのコストと、 実施したことによるメリット ( トータルでの生産性向上やバグの削減など ) を天秤に掛けて決定することが多いです。 例えば、 現時点では、 GUI 部分を TDD で行うことは、 テストコードの作成が困難で時間が掛かるため、 一般的には手動テストを行うことが多いようです。 これも、 アジャイル開発手法から生まれてきたためでしょう。 いかに早く ( したがって、 低コストで ) 動くプログラムを納めるかに価値を見出しているアジャイル開発では、 教条的に 「どんなときでも~すべし」 と言うことはありません。

5. TDD のメリット

テストを先に書く

  • メソッドレベルの外部設計を考えることになる。 それは、 使いやすいメソッドになるであろう。
  • テストを通るだけのコードを書く。 それは、 不要な部分を含まないコードになるであろう。 テストされないコードが減るから、 最終的なバグも減るであろう。
  • テストしやすいコードになる。 それは、 疎結合でシンプルで小さいクラスになるであろう。

コードとしてテストを書く

  • いつでもすぐに再テストできるようになる。 それは、 「動いているコードを触るな」 という禁句を古いものとし、 リファクタリングを正当化する。
  • それはまた、 バグを修正した後の回帰テストもすぐに終わることになる。
  • 多くのテストを短時間で実施できるようになる。 それは、 問題点を見つけ出しやすくなるであろう。

短時間で繰り返す

  • すぐにフィードバックを得られる。 それは、 気分が良いだけでなく、 設計する速度を加速するであろう。
  • TDD 中にバグが出たとき、 その原因は数分以内に自分が行った作業であることが多いであろう。 それは、 バグの原因追求に掛ける時間を短縮するであろう。
  • 帽子を次々と被りなおして、 違う視点でコードに取り組む。 それは、 よりよいプログラムを生み出すであろう。

最大のメリットは、 詳細設計書の一部 ( あるいは、 ほとんど全部 ) を書かなくても済むようになることかもしれません。 すなわち、

  • テストコードには詳細な仕様が表現されている。 それは、 ( 嫌な ) 文書作成作業を減らし、 ( 大好きな ) コーディング作業を増やせることになる。

6. TDD の効果

開発プロセスを管理する立場から見ると、 次のような効果が期待できます。

・ 品質の向上

先にテストコードを書くことにより、 仕様をレビューしたのと同様な不具合数の減少が見込めます。 また、 実際にテストを行うのですから、 不具合の減少が見込めます。
レビューやテストを 1段実施すると、 潜在不具合の 3割は発見できると言われていますので、 テストファーストを実施することで 2段分、 つまりバグの発生が半分以下になると期待できます。
また、 先にテストを書き、 こまめにリファクタリングも実施することから、 不具合以外のコードの品質も向上することが期待できます。

・ 生産性の向上

「実装」 に掛かる時間は増えますが、 「結合テスト」 以降でのバグ検出数が減り、 トータルでは工数が減ると期待できます。
TDD 導入当初は不慣れなため、 あまり減らないか逆に増加することもありえますが、 慣れるにしたがってトータルの生産性は向上します。

※ なお、 「実装」 工程だけの 「生産性」 を取り出して計測する ( つまり、 単位時間当たりに書いたコード行数などを測る ) ことが多いようですが、 生産性を計測するにあたって品質を考慮しないのはナンセンスです。 一定の品質に到達していなければ、 出荷できないのですから ( 出荷できなければ、 生産性はゼロです )。

7. TDD の前提となるスキル

TDD は誰でも出来るとは言えません。 プログラムが書けるというだけでなく、 さらに次のようなスキルが必要とされます。

・ テストを書くためのスキル

メソッドやクラスの外部設計を行うスキル。 そのスキルには、 同値分析や境界値テストといった、 テストケースを作るスキルも含まれます。

・ リファクタリングするためのスキル

リファクタリングすべき、 よろしくないコードを見分けるスキルと、 実際にそのコードを改善するためのスキル。


Copyright © 2009 biac All rights reserved.