TDDを学ぶべき10の理由 #TddAdventJp

かなり香ばしいタイトルですが、TDD Advent Calendar jp: 2011のエントリーとなります。前日の@bleisさんのエントリーの次になります。

はじめに

TDD(テスト駆動開発)とは、「テストファーストを原則とし、テストが成功するようにプロダクションコードを書くというサイクルを繰り返す開発手法」です。XPのプラクティスの1つとして10年近く前に紹介され、ここ数年で再び1つのムーブメントとなっています。これは、TDD Boot CampがTDDへの敷居を下げ、体験する機会を提供した事も1つの大きな要因でしょう。
自分もTDDに魅せられたエンジニアの1人です。ぶっちゃけ、TDD信者とかTDD厨とか言われても可笑しくはありませんし、むしろ嬉しいくらいです。一方で、TDDを嫌う人もいるのも事実です。しかし、自分もTDDを銀の弾丸とは思っていませんし、適用しにくい領域もある事も理解しているつもりです。それでも、プログラマはTDDを学ぶべきだし開発で実践するべきというスタンスは崩れません。
今日のエントリーは、なぜTDDを学ぶべきなのかという理由について考察してみたいと思います。一部ユニットテスト自体のメリットも含まれます。

1.フィードバックと安心

TDDで開発を行わないとした場合でもユニットテストの最大のメリットは、プログラムとしてテストが可能となる事です。これは、テストを手動で実行する場合に比べて、テストコードを作成するコストはかかりますが、実行コストを限りなくゼロにする事が出来き、何度でも繰り返してテストを実行できるようになります。したがって、コードの追加や修正毎にテストを実行する事ができます。
このように継続的にユニットテストを実行できるようになれば、コードの追加や修正が他のコードに影響を与えている場合にテスト失敗として認識できるようになります。この問題の素早いフィードバックがあるため、開発者は安心して新機能の追加や仕様変更を行う事ができるようになります。

2.リファクタリング

ユニットテストがある事で、素早いフィードバックを得る事ができるため、変更に対する恐怖がなくなります。これは、積極的にリファクタリングが出来るという事です。
リファクタリングとは、「プログラムの外部的な振る舞いを変えずに、内部構造を変更すること」です。ユニットテストトは「プログラムの外部的な振る舞い」を検証する事に他ならないため、ユニットテストが成功する事を維持するのであれば、安心して内部構造を変更できるのです。
ソフトウェアが健全な状態を保つには、定期的なリファクタリングは必要不可欠です。開発がYAGNI(You ain't gonna need it)の原則に従っていれば、必要になってから必要なものを付け足していくはずです。したがって、はじめはシンプルなものであっても次第に複雑になるのです。一般的に、開発が進めば進むほどプログラムは複雑になるでしょう。デザインパターンなども最初から適用するのではなく、必要になってから適用する方が良いケースが多いです。そのような時にユニットテストがある事で、大胆な内部構造の変更を安心して行えるのです。
定期的なリファクタリングを行い、ソフトウェアを少しずつ作っていくことと健全な状態に保つ為にはユニットテストが必要です。

3.インターフェイスへの意識

TDDではテストファーストが原則です。プロダクションコードよりも先にテストコードを書くため、そのクラスがどのようなメソッドを持つかを考えなければなりません。メソッド名・引数・戻り値・副作用などを意識し、先にテストコードに書いていくため、自分が最初の利用者となります。使いにくいインターフェイスであれば自分を苦しめます。
コードを書くときにインターフェイスを意識するべきというのは当然の事のようにも思えます。TDDを行わない場合、特にプログラミングの経験が浅い人ほどインターフェイスの設計よりも先に実装したい機能に意識が向いてしまいます。その意識を強制的に「どう使われるか」に向けるのがTDDのテストファーストなのです。

4.シンプル設計

TDDではテストコードを書き、そのテストを成功(グリーン)にするために最速な実装を行う事が原則です。これは、TDDのサイクルを短くしリズム良く開発を進める効果も大きいですが、最大の効果は実装がシンプルとなる事です。シンプルであるほどバグが少ないことは言うまでもありません。
また、TDDではテストコードを書かなければならないため、設計をシンプルに保たなければテストが書きにくくなります。特にクラスの結合度が高い場合や内部状態を持つ場合などは、疎結合で内部状態を持たないクラスに比べてテストが複雑になり避けるようになります。TDDを行っていると常にテストし易さ(テスタビリティ)を意識してクラス設計を行えるようになります。内部状態や結合度が強い部分は、本当に必要な部分だけになるでしょう。
TDDを実践する事で、無駄なコードが少なくなり、ソフトウェア全体がシンプルになります。

5.メンテナンスされたドキュメント

私はドキュメントを書くよりもコードを書いていた方が幸せです。できれば、面倒なドキュメントを書きたくはありませせん。基本設計やユースケースレベルのドキュメントであれば苦ではないのですが、APIレベルのドキュメントを書くのはもの凄く億劫です。恐らくは、「それコードで書いた方が早いし、楽だよ...」と感じてしまうからだと思います。
そう、TDDを行う事で、APIのドキュメントは動くテストコードとして書くことが出来るのです。具体的な入力値と期待値もドキュメント化され、それが正しく実装されている事も保証されます。また、テストコードはそのAPIのサンプルコードとしても読むことができるため、使い方が解らなかったらテストコードを読んでみるという事が出来るようになります。
さらに、ドキュメントは書くことよりもメンテナンスする事の方が難しいものです。仕様が変わった時や追加された時にドキュメントの更新を忘れる事は良く発生しますし、ドキュメントを更新しなくともソフトウェアの不具合とはなりません。後日、そのドキュメントを読んだ人が誤解してしまい、大きな不具合を混入させるかもしれないというリスクだけが蓄積されていくでしょう。
ドキュメントがテストコードとして作成されたならば、メンテナンスは必要不可欠となります。もし、メンテナンスしなければテストは失敗するようになるでしょう。
TDDを実践する事で、必要なドキュメントをメンテナンスされた状態で持てる事になります。

6.具体的なテストデータ

ユニットテストを行う時には具体的なテストデータを準備しなければなりません。例えば、「加算する」ではなくて「3と4を加算すると7となる」と言ったように具体的な値がなければテストケースになり得ません。些細な事と思う人もいるかもしれませんが、ソフトウェア開発では具体的でリアルなデータの有無は品質と生産性に直結する重要な要素です。
具体的でリアルなデータが重要な事はユニットテストだけに限らず、ユースケースシナリオ・機能テスト・受入テストなどの作成でも同様です。ユースケースシナリオ等では、リアルなデータである事により顧客が理解しやすくなります。例外となる状況や見落としてしまうようなビジネスルールに気付くこともあるでしょう。ユニットテストでも同様で、開発者同士で具体的な値を示してコミュニケーションを行う方が相互理解が深まります。
TDDを行えば、プロダクションコードを書く前にどんなデータが必要かを考える事が自然となります。

7.習得可能なスキル

「メリットは解った。でも、難しいんでしょう?」
はい。TDDは簡単なスキルではありません。TDDを行うには、テストに関するスキル・設計に関するスキル・リファクタリングに関するスキルなど多くの知識・スキルが必要です(see: TDD の基礎体力と、TDD に対する想い)。TDDのやり方を知ったとしても、自然にテストを書くようになるには練習が必要です。
しかし、安心してください。TDDは「量が質に転化する」スキル(by @t_wada)です。TDDと関連する基礎スキルを伸ばすことはプログラマとしてのスキルを伸ばすことに繋がります。プログラマとしてのスキルを伸ばす必要がない人にはTDDは不要ですが、TDDはテストスキル・設計スキル・リファクタリングスキルを伸ばすには都合が良い開発手法です。
TDDを効率良く学ぶには、経験者とペアプログラミングする事が最も効果的です。経験者がどのように考えて、どコードを書いていくかを感じましょう。職場でそのような環境がないのであれば、TDD Boot Campに参加して、まずは体験する事が良いと思います。勿論、独学でもTDDは習得できます。「テスト駆動開発入門」や「和田卓人の“テスト駆動開発”講座」を参考に写経してみることです。
TDDに関するスキルが習得可能である大きな理由は、ある程度はパターン化可能だからです。プロダクションコードは要求次第でどのようにも変わる可能性があります。しかし、ユニットテストのパターン、設計に関するパターン、リファクタリングに関するパターンは1つづつ習得していく事が可能なパターンです。量が質に転化するのは、パターンの引き出しが増えるからです。気がつけば「これはこうする」といった思考が自然にできるようになるでしょう。
TDDはプログラマとしてのスキルを確実にアップさせる事ができます。

8.環境へ非依存

アジャイル開発を行おうとすれば、顧客を巻き込む事が必要不可欠です。これは様々な理由で高いハードルとなります。しかし、TDDは開発手法の1つでしかありません。言い換えれば、どのようにしてプログラミングを行うかという事です。したがって、あなたの開発現場で明日からでもTDDをはじめる事ができます。
勿論、TDDの効果を実感できるまでには相応の経験と知識が必要です。しかし、導入するだけであれば、開発チームの合意を得られれば、それこそ1人でこっそりテストコードを書くこともできます。
TDDは開発者の気持ち次第で簡単に導入できます。

9.たくさんコードを書ける

プログラマであれば、コードをたくさん書きたいと思いませんか?
TDDではテストコードを大量に書く事が求められますが、言い換えればたくさんのコードを書くことができるという事です。テストコードもプログラムですから、工夫して整理しなければメンテナンス性を損ないます。効果的なライブラリやツールを作成したりする事もできます。これらのコーディングはプロダクションコードのコーディングよりもずっと自由があるでしょう。
プログラマであれば、コードを書くことは楽しい事です。TDDを導入すればたくさんコードを書けるようになります。

10.開発が楽しくなる

追い詰められた状態で開発をしても、生産性はあがるどころか下がるだけです。後からテストを行ってたくさんの不具合に怯え、デグレに気をつけながら修正せいたり言い訳を考えるのは気が滅入ります。それよりも、TDDを導入して安心を手に入れましょう。
安心を手に入れ開発に余裕が生まれれば、自然と生産性も高まり品質も高まります。顧客からの仕様変更も笑って受けられる体制であれば、お互いに幸せになれるはずです。それをコードをたくさん書くことで得られるのであれば、プログラマにとって、とても幸せな事です。

まとめ

TDDは、銀の弾丸ではありません。GUIのテスト、仕様が曖昧であったりプロトタイプとして作成しているソフトウェアなどには仕様変更による修正コストが高くつくため適用範囲を見極める必要があります。TDDを行ったからと言って品質が高まる保証もありません。プログラマのスキルが高まり安心して開発できるようになるため、結果として品質が高くなります。また、テストなんか書かなくても神のような速度で完璧なコーディングができる人やプログラマとしてスキルを高めたくない人には効果が薄いでしょう。
TDDは、自分のスキルを高めたいプログラマにとっては、楽しく効率良くスキルアップできる強力な開発手法です。今日からでもTDDを学び、そして広めていってください。開発現場をもっと楽しくしましょう!

次は、@sue445さんのエントリーです。