はじめに
本連載では、「透明性」というキーワードで、アジャイル開発について説明しています。第四回目は、開発者の視点からの透明性について、テスト駆動開発(TDD)をキーワードに考えます。
ソフトウェア開発においては、コミュニケーションギャップにより、最終的な成果物と顧客のニーズとの食い違いを生み出す原因となっています。
以下のような箇所でギャップが発生します。
前回までに、顧客と開発者の間のギャップを如何に埋めるかについてご説明してきました。適切なイテレーション設計でフィードバックの連鎖をつなげることと、タスク管理をより透明度の高い方法で実施することの重要性について紹介しています。今回は、開発者内部でのコミュニケーションギャップにスコープを当ててみたいと思います。
設計の共有の重要性
事前に詳細を全て決定し、ドキュメントを起こしてから開発に入るウォーターフォール的なやり方で、結果的に顧客のニーズにそぐわないソフトウェアを作ってしまうといった失敗が、数多くありました。この失敗の原因のひとつに、ドキュメント中心の開発手法では、開発中の仕様変更に対応し難いということがあげられます。
大量のドキュメントは、更新コストが相対的に大きくなりますので、アップデートが遅れ、結果、最新状態の文書がどこにあるのか分からなくなります。このような、ドキュメントに依存した情報伝達の弊害は特に変化の激しい状態において顕著となります。それに対して、ドキュメントの記載は最小限にとどめ、その代わりに対話を増やす等、情報伝達の実効性を上げる方向で、ソフトウェア開発の透明性を高めようとするのがアジャイル開発の考え方です。
この場合、特に注意が必要となるのが設計情報です。誤解を恐れずに敢えて言いきってしまうと、アジャイル開発では、ドキュメントは必要最小限しか用意しません。その為、設計の細部はドキュメントには載っておらず、実装担当者に任されています。それらの情報をどうやって共有するのでしょうか? コードには全て記載してありますが、本当に分かりやすいコードを記載するにはコストがかかります。それを読み解くには、ある程度開発言語の知識も必要となるでしょう。
さらに致命的なのは、コンテキストの説明ができないことです。何をしているかはコードから読み取れたとしても、なぜその処理が必要になるのかの背景については、コメントで残すしかありません。
設計に関する情報を、一元的に整理することが必要です。アジャイル開発ではテスト駆動開発を採用することで、その問題に対応しています。今回はテスト駆動開発について、少し詳しく見てみることとします。
テスト駆動開発(Test Driven Development) とは
テスト駆動開発(TDD)とは、開発者自らがテストを先に書き、その書いたテストがパスするように後から動作するソフトウェアを作っていくというやり方です。
事前にテストを作成するというと、仰々しいテスト仕様書を先行して作成するようなイメージがありますが、テスト駆動開発で意味するテストは、そうではありません。開発者自らがテストの作成とソフトウェアの実装とを行います。テストと実装のサイクルは、頻繁に回転させるもので、ひとつテストを書いては、対応する実装を書き、テストをもう一つ書いたら、次の実装を行うというものです。そして、実装に対してリファクタリングを繰り返し、よりよい設計へと進化させてゆきます。ここで書かれるテストは、全て自動化され、何度でも繰り返し実行可能になっている必要があります。クリック一つで全てのテストが走るようでないと、実装作業の中にストレス無くテストを挟み込むことが難しいからです。このように、テスト、実装、リファクタリングのステップを、リズミカルに回していくことで開発を促進することが テスト駆動開発のやり方です。
テスト駆動開発の2つの側面
テスト駆動開発には、テストとしての品質保証の側面と、設計作業の表出化という二つの側面があります。品質保証の側面とは、テスト駆動開発を実施することで単体テストを事前に用意しますので、単体テストの網羅率が高くなり、ソフトウェアの品質が上がるというものです。具体的には、結合テストや統合テストといった最終的な品質保証に必要なテストを実施した際のバグの発生率が減少し、それらのテスト工程に必要な工数は激減します。これは非常に意味のある効果ですが、あくまでもテスト駆動開発の一側面に過ぎません。アジャイル開発で重要となるのが、後者の設計作業としてのテスト駆動開発です。
例えば、オブジェクト指向開発において、あるクラスを作成するとします。テスト駆動開発においては、実際のクラスを作成する前に、そのクラスに対するテストを書きます。これは、そのクラスが他のクラスとどのように相互作用するのかを決定する作業となります。つまり、クラスの振る舞いを定義していることと同義です。
そしてアジャイル開発においては、あるクラスの振る舞いの詳細を確認したい場合には、詳細に記述されたドキュメントではなく、このテストを参照することとなります。つまりテストはコミュニケーションツールとして、設計情報の伝達に貢献するのです。
その為、テスト駆動開発におけるテストは、品質を確認できれば良いというものではなく、よりテスト対象のクラスの振る舞いを明確にするように書く必要があります。
テストで設計することのメリット
なぜ、設計書でなく、テストを設計書の代わりにしようとするのでしょうか?それは、コードとの整合性を検証可能(実行可能)な設計書となるからです。
要求仕様に沿ったコードが作られていることを確認する為には、従来は、要求仕様と設計書の整合性と、設計書とコードの整合性の二つを確認する必要がありました。ウォーターフォール型の開発手法では、設計フェーズで設計書を作成し要求仕様との適合性を確認します。そして開発フェーズでコードを作成し、設計書との適合性を確認します。このように段階的に信頼性を伝搬していく仕組みは、構造的に手戻りに非常に弱くなります。要求仕様に変更があると、変更の反映作業が複雑になり管理コストが膨れ上がるという問題が発生するからです。つまり、設計書の変更がコードに反映されていなかったり、コードが変更されても設計書のアップデートがなされていなかったりする現象が多発します。
テスト駆動開発のように、設計書を自動化テストにしてしまえば、コードと設計書の間の食い違いは、テストを走らせることで常時確認が可能となります。後は、設計の要求仕様への適合性を確認できれば、仕様変更への追随をより少ないコストで行うことができるのです。
アジャイル開発において、ドキュメントを書かないとは、このように設計をテストで表現するということを意味します。文章で詳細な仕様を表現したとしても、変化への追従コストが高くなってしまいます。そうではなく、実行し整合性を常に検証可能なテストで設計を表現し共有することで、常に最新の状態の設計資料を維持することが可能となります。つまり、詳細を目に見える形で記録しないのではなく、いわゆる従来型のドキュメントとは異なる形で、記録・共有しようとするものです。
目的は変更コストを下げることですので、仕様変更への追従コストが問題にならない形で文書を書くことは、透明性を高めるという意味でむしろアジャイル開発においても推奨されるべきものです。具体的に何を書いて何を書かないかは、プロジェクトの複雑さ、人間関係、変更可能性等を考慮して、判断すべき内容となります。
テスト駆動開発で作成するテストの範囲
テスト駆動開発の大きな目的の一つに、設計を検証可能な形で残すことがあることをご説明しました。また、品質保証目的でテストを充実させる効果についてもご説明しました。
しかし、これらの二つの目的は、時に相反する内容を含みます。すなわち、前者はコーディングと並行してテストを作ることで、進化的に設計を成長させてゆく作用を重要視しています。その為、リズミカルにテストと実装を反復する必要があります。おのずと粒度は小さく単体レベルのテストに限定されてしまいがちです。
対して、品質保証目的のテストは、証跡を残したり、本番と同様の構成で検証したりする必要があります。テストの粒度も、結合テスト以上の比較的大きなテストとなるでしょう。
テスト駆動開発に品質保証に必要なテストを網羅することを期待しすぎると、開発を促進するのに必要な軽量感が損なわれてしまいます。それは進化的設計・開発を駆動するといったテスト駆動開発の趣旨を損なうもので、避けるべきです。このように実装作業として並行して作成する自動化テストだけでは、品質保証の観点からすると不十分なテストとなりがちだとの批判もありますが、そもそも品質保証に必要なテストを充足させることを目的としたプラクティスではありません。品質保証を目的としたテストは、リリースの前に実施することになりますが、その時点のコストを引き下げる効果があり、その意味で品質保証に貢献するという意味になります。
大切なことは、設計作業としてのテストが開発を駆動することです。
テストのレビュー
このようにアジャイル開発手法では、テスト駆動開発を行い、テストを設計書とするので、仕様変更に強いことをご説明してきました。しかし、肝心のテスト=設計が仕様通りになされていることはどうやって確認するのでしょうか?単純な部分については、テストやテストデータを自動生成することはできても、本当に重要な部分については、人手により検証することが欠かせません。
一つは、テスト作成時に検証する方法と、もう一つはテスト作成後に検証する方法です。前者はペアプログラミングであり、後者はテストインスペクションです。
例えば、XP では、二人でコードを作成します。これは知識の伝達とかリファクタリングの方向性を揃えるとか様々な効果を持ちますが、一義的には開発者による仕様の取り違えの防止です。設計と実装を細かく同時並行的に進めますので、正しい仕様の理解で設計・実装を行っていることを複数人でチェックしあいながら進めることが目的です。
またペアプロのようなプラクティスをもたない開発手法では、テストレビューやインスペクションを強化しています。例えば、FDD(Feature Driven Development) では、コーディングでコードインスペクションを通過しないかぎり進捗率が100%にならないよう、プロセスで定められています。
また上述のように、テストが設計となっていますので、テストインスペクションといっても、品質保証におけるテストインスペクションと異なり、設計の妥当性やテストデータの正当性といった観点がより強調されることになります。
まとめ
アジャイル開発において、テスト駆動開発を行うということは、設計を検証可能な形で表出化するという意味を持っています。設計情報を共有し、開発者間の透明性を高めるという意味で、テスト駆動開発はアジャイル開発において欠かせないものとなっています。次回はもう少し具体的な開発者間の透明性を向上させる為の方法について、ご紹介したいと思います。