僕がTDDするモチベーション
長らくTDDの恩恵を受けて開発して来たので、自分なりにその良さというか、何が引き続きやっていこうというモチベーションになっているのかを考えてみました。
なお、TDDが何であり、何ではないか、というところについてはここでは深く触れません。
(テスト駆動開発の手法そのものに興味がある方は、書籍をどうぞ)
次に何をやるか、そしてそれが終わったかが明確になる
テストを書いて、実装をして...の繰り返しがTDDの開発サイクルです。
このサイクルに則ることで、次に何をすべきかが明確になります。
例えば、作るべきものは決まっているが、実装がまだ白紙の状態では、まず、すべきことは最初のテストを書くことです。(設計の粒度や手法はプロジェクトによってそれぞれだと思いますし、ここでの主題ではないのであまり触れませんが、何かしらの設計はある前提です)
ボトムアップで個々のモデルから組み上げる場合でも、トップダウンでユースケース側から掘り下げる場合でも、TDD的アプローチで進める際の考え方に違いはありません(テストケースの書き方は変わるかもしれません)
重要なことは、「次にやることを考えられなければ、まずテストが書けない」ということです。
テストファーストなアプローチの始点は、まず実装すべきものが何かを考えること。
ロジックの輪郭をイメージ出来ないと、動き出せないです。これは、絵を描くときのデッサンに似ているかな、と思います。
いきなり「画面の部品が」とか「データベースのスキーマが」というような「実装の詳細」から着手してしまうのは、キャンバスの上にいきなり緻密に描き込まれた「目」とか「手」とかをポツンと配置して着色までするようなもの。もちろん、そこからバランスの取れた「全身」を描ける人も当然います。当然いますが、そういった人は、頭の中ですでに全身をどう描くかイメージ出来ているタイプの人です。多くの人はそんな芸当は難しいのではないでしょうか。少なくとも、自分には無理です。
テスト自体をテストしながら実装を進められる
テストを先に書くと、必ずテストが落ちます。落ちないテストはバグっています。実装していないのに通っているテストなわけですから。
この過程が大事で、テストを書くときに、何が必要かをきちんと考え、そして、それが満たされていないことを確認した上で実装に入るのです。
もたろん、あとから書き足したテストでも、上手くいくかもしれません。が、実装ありきのテストは、無意識のうちに実装に寄った書き方に
なっているかも知れませんし、ともすればバグっていて、意図した実装に対するチェックの機能を満たしていないかも知れないのです。
コードがテストしやすい構造になる
テスト出来ないと前に進めないわけですから、プロダクトコードはテストコードと共に成長します。そして、テスト出来ない(しにくい)コードの書き方を避けるようになります。必然的にテスタビリティも向上し、保守性の面でも大きく寄与します。
TDDのアプローチと、単体テストの後付けのアプローチとの大きな差はここに出ると思います。
ただし、TDDのアプローチによるテストはあくまで開発手法としてのテストです。品質を担保するためには不足している可能性もあるので、足りない部分を必要に応じて後付けのテストで補うのは良いことだと思います。
逆に品質を担保するためのテストをTDD的アプローチで書こうとするのは、(コンテキストにもよりますが)目的と手段の不一致を感じます。
解くべき問題を局所化し、粒度を小さくできる
小さいサイクルで実装を進めるということは、問題領域を小さく細分化した上で作業に取り掛かるということ。
そうすることで、自然とコードの単位が適切な粒度になっていきます。
「メソッドはこれくらいの長さまで」という規約で縛るのは、本質的ではないと思っていて、それは実装内容の話ではなく、あくまでコードの表現上の話だから。
それに対して、小さくテストするサイクルを回すアプローチは、実装の意味に基づいています。
例えば、自動車を製造するとして、全てを組み上げてしまってから、ちゃんと走るかだけテストする、というわけにはいかないでしょう。当然、部品の時点できちんと規格通りかチェックします。
組み上げたものがきちんと動くかは当然、最終チェックをします。ですが、それは、事前の工程で積み上げたものが想定通り動いている前提の上に成り立っています。そうしないと、チェックをパスしなかったときの手戻りが大きすぎる。
TDDが小さいサイクルを回すので、そう言った異常検知なども小さい単位で検知できます。
あとがき
これからTDDをやってみたい、という方に手段と目的を混同しない、ということをお伝えして終わりたいと思います。
重要なのは、テストを書いているかどうかではなく、そのテストが何に貢献しているか、を意識すること。
品質なのか、開発サイクルの効率化なのか、その他の何かなのか。
当然、その目的は、それぞれのプロジェクトによって異なります。
自分の場合は今回書いたようなモチベーションでTDDに取り組んでいますが、これはあくまで「僕の場合は」というコンテキストでの話です。
それぞれの環境、チーム、個人で色々なコンテキストがあると思います。手段は適材適所で選んでください。