TDD三原則
http://www.butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd の翻訳です。
ぼくはいままで、テスト駆動開発を3つのシンプルなルールで説明してきた。
- 失敗するユニットテストを成功させるためにしか、プロダクトコードを書いてはならない。
- 失敗させるためにしか、ユニットテストを書いてはならない。コンパイルエラーは失敗に数える。
- ユニットテストを1つだけ成功させる以上に、プロダクトコードを書いてはならない。
なによりも最初にユニットテストを書いて、必要な機能の実装作業をはじめる。第2のルールにより、ユニットテストは少ししか書けない。ユニットテストがコンパイルに失敗したり、アサートに失敗したら、ユニットテストは止めにしてプロダクトコードを書かなくてはならない。第3のルールにより、テストがコンパイルできるか通るようになったら、プロダクトコードはそれ以上書けない。
以上のことから考えると、コンパイルしたり実行したりせずに、まとまった量のコードを書くのはそもそも不可能である。狙いはそこにあるのだ。なにをするにせよ、テストを書くのであれ、プロダクションコードを書くのであれ、リファクタリングでも、常にシステムを実行しつつけるのだ。テストを実行する間隔は秒か分の単位だ。10分では長すぎる。
ボーリングのカタ(英語)の実例を読むと実感がわくだろう。
さて、ほとんどのプログラマーはきっと「バカバカしい」「作業が遅くなり、時間と労力のムダが増え、思考が途切れてしまい、設計もできなくなり、集中さえできなくなってしまう」と思うはずだ。だが考えてみてほしい。全員が上記のやり方で作業しているとすると、その中の誰でも、いつ聞いても、ほんの1分前には全コードがちゃんと動いていたことになる。
繰り返す: 1分前にはすべてのコードが動いていた!誰でも構わない、いつでも構わない。1分前にはすべて動いていたのだ!
いつでも1分前にはコードが動いていたら、デバッガを使う機会はどのくらいあるだろうか?「めったにない」が答えだ。CTRL-Zを何回か押してコードが動いていた状態に戻して、最後の1分間の作業をやり直すだけでいい。デバッグがなくなったら、どれだけ時間が節約できるだろうか?きみは現在、デバッグにどのくらい時間を費やしている?デバッグ後、修正するのにかかる時間はどうだろうか?そうした時間を大幅に、何分の1かにできるとしたらどうだろう?
メリットはこれだけではない。このやり方では、1時間単位に数個以上、テストを生産する。1日なら10数個になる。1ヶ月なら数百だ。1年間続ければ、何千ものテストを書くことになるのだ。テストはすべて取っておけるし、いつでも実行できる!いつ実行するのか?常にだ!どんなに些細であれ、変更したらテストを実行するのだ!
汚いコードを直さないのはなぜか?壊してしまうかもしれないからだ。だがテストがあれば、コードを壊していないと十分に自信を持って言える。壊したら、すぐに気がつく。テストがあれば変更を恐れずにすむ。汚いコードや、わかりにくい構造があったら、なんの心配もせずに修正できるのだ。テストのおかげでコードは柔軟になる。テストのおかげで、ソフトウェアは本当にソフトになるのだ。
いいことはまだある。あるAPIをどう呼び出すかわからないとき、呼び出し方法の書いてあるテストがある。あるオブジェクトの生成方法がわからないとき、それが書いてあるテストがある。既存のシステムについて知りたいことがあるとき、やりかたを示すテストがある。テストは設計ドキュメントでもあり、コード例でもあり、システムの働きと使用方法を示してくれるのだ。
サードパーティのライブラリを自分のプロジェクトで利用したことはあるだろうか?ライブラリにはすばらしい、分厚いマニュアルが付いてきたはずだ。マニュアルの末尾には、コード例がいくつか載っている。さて、あなたならマニュアルのどこを読む?コード例に決まっているだろう!これがまさにユニットテストなのだ!ドキュメントの中でもっとも役に立つのがコード例だ。コードをどう使うのかの、生きた例なのである。ユニットテストは設計ドキュメントの中でも、きわめて詳細で、曖昧性がまったくなく、実行できるくらい形式的であり、プロダクションコードと乖離することもないのである。
さらなる利点も見てみよう。既存のシステムにユニットテストを追加しようとしたことがあるだろうか。あまり楽しい仕事にはならなかったはずだ。ユニットテストを追加するために、既存の設計を一部変更しなくてはならなかったり、テストの側でトリックを使わないといけなかったりしたのではないだろうか。テスト可能なように設計されていないシステムにテストを書こうとすれば、当然のことだ。たとえば、あるメソッド「f」をテストしたいとする。しかし「f」は他のメソッドを呼び出しており、そのメソッドはデータベース上のレコードを削除してしまう。テストではレコードを削除したくないのだが、止める方法がない。テスト可能な設計になっていないシステムなのだ。
TDDの三原則を守っていたら、定義により、コードはすべてテスト可能なのだ!「テスト可能」ということは「疎結合」ということも意味する。あるモジュールを単独でテストするのに、結合されていては無理である。したがってTDDは結合を減らし、モジュールの独立性を高めることも強制するのである。実際に3つのルールをきちんと守っていると、今まで以上に結合度を低めるようになることに気づくはずだ。TDDにより結合度の低い、よい設計が得られるのである。
以上で説明した利点を考えれば、TDD三原則のバカげたルールは、実際にはバカげていないことがわかるはずだ。むしろ基本的かつ有用なルールである。僕はプログラマーを30年近くやって初めて、TDDに出会った。当時僕は、誰かから基礎的なプログラミングの手法を教わることなどあり得ないと思っていた。30年というのはそうとうな経験だ。しかしTDDを始めてみると、僕はTDDの有効性に仰天した。虜となったのだ。それ以来僕は、長いプログラムの固まりを、うまく動くように祈りながら書くなんてことができなくなった。モジュール群を整理して、次の金曜日までに再構築しようなどと考えることもできなくなった。僕がプログラミング中になにか決断するときは必ず、1分後にちゃんと動作するかどうかが絶対の基準なのだ。
![(please configure the [header_logo] section in trac.ini)](/trac/public/chrome/site/AgileEnP.jpg)