テスト駆動開発には「リズム」と「サイクル」があります。
リズムについては前回説明しましたので、今回と次回でサイクルの話をします。
テスト駆動開発のサイクル
テスト駆動開発のサイクルとは、1つの機能を実装するにあたって、どんな手順を踏んで、どういう回し方をしていくかということです。たとえば、ある1つの機能を実装したい、提供したいということになったときに、まずどういうテストを書いて、それからどういうコードを書いていくのか。
今回は、テスト駆動開発のサイクルとしてまず最初に受け入れテストを土台として作るという話をします。
そして次回、その受け入れテストを通すために、どのようにレッド、グリーン、リファクタリングというサイクルを回していくのかというお話をします。
なお、ここで説明する回し方の対象は、スタックや足し算といった、書籍のサンプルでよく登場するような小さい規模のプログラムではありません。それよりももっと現場に近い、実際の小さいプロジェクトをまわすときとか、『WEB+DB PRESS Vol.35』の特集1「実演! テスト駆動開発」や『WEB+DB PRESS Vol.37』の特集1「実演! リファクタリング」でやったような、若干規模が大きいプログラムに対してどう取り組むかです。
まずは受け入れテストを書く
まず私がテスト駆動開発のサイクルの一番最初として書くのが、受け入れテストです。
これからシステムを作っていきたい、あるプログラムを作っていきたいというときには、当然ながらまだそのプログラムは1行も存在していません。
1行も存在しないときに、あたかもそのプログラムがあるかのような形で、プログラムに対する「外側の視点からの」テストを書きます。要するにプログラムの実装がどうなっているかをまったく考えないで、「こうなるべき」「こうなってほしい」という視点のテストをまず最初に書きます。
このように実装をまったく意識せずに「外側の視点」から行うテストを「ブラックボックステスト」と呼ぶことがあります(逆に、実装コードに対する視点のテストを「ホワイトボックステスト」呼びます)。
受け入れテストと機能テスト
ここでの「受け入れテスト」という言葉の使い方について、少し補足します。
連載第4回で、受け入れテストはCustomer Testing、機能テストはDeveloper Testingに分類されるという説明をしました。でも、この2つのテストは、実は形としては似ていることが多いんです。役割としてどちらの役割か、ということが分類の違いとして現れているだけなんです。
今回の文脈で言えば、「自分がこれから開発するために」という視点ですので、正確に言うと、私が最初に作るのはDeveloper Testingである機能テストと言えます。
かつ、その機能テストが動いたら、私が今開発しているプログラムは機能が達成されたという意味合いにもなりますので、より正確に言うと、「Customer Testingである受け入れテストの意味合いも持っている」機能テスト、となります。
簡単のため、本連載では、このようなテストを「受け入れテスト」と呼ぶことにします。
受け入れテストはEnd-to-Endのテストで
では、『WEB+DB PRESS Vol.35』の特集1「実演! テスト駆動開発」を例に、具体的に説明していきます。
『WEB+DB PRESS Vol.35』のサンプルは、小さな通信用のプログラムでした。ブラウザが、サーバに対してあるURLでGETアクセスをかけると、そのURLに対応したXMLを返すよ、というプログラムでした。
全8章をかけてこのプログラムを開発する際に、一番最初に作ったのは、このプログラムがもしできあがっていたとしたらこう動いてほしいなという視点からのテストでした。つまりサーバ側のプログラムがまったく存在しない状態で、サーバプログラムに対してアクセスしてテストするコードを書いたわけです。
これからサーバ側に、小さな役割に従った小さなプログラム群を複数組み合わせて機能を開発しなければならないのはわかってるけれども、まだ実装についてそこまでつっこんで考えるという段階ではないわけなんです。ですので、実装に深く入り込むのではなくて、もしサーバ側プログラムができあがったらこう動いてほしいなという、ToBe(こうあるべき)の視点でテストを書きました。
このようなテストを、「End-to-Endのテスト」といいます。End-to-Endのテストとは、リクエストがクライアント、たとえばブラウザから始まって、サーバ内で処理され、制御が再度クライアントのほうに返ってくるという構成のときに、リクエストを投げてレスポンスが返ってくるまでの処理をブラックボックスとして捉え、大きく機能の入り口から出口までの範囲のブラックボックステストを行うことを指します。
HTTPUnit
WEB+DB PRESS Vol.35で実際にどうやったかというと、Javaの世界ではHTTPUnitというものがありましたので、そのHTTPUnitを使って、まだ存在しないサーバプログラムにアクセスして、XMLが返ってくるべきというテストを書きました。
サーバサイドのコードは1行も存在しませんので、当然テストは赤くなります。レッドバー、テストは失敗したわけです。
レッドバーを確認したうえで、ではそのテストをどうやって成功させればよいか、どうやってグリーンにしていくかという視点で、これから開発するサーバ側プログラムをもうちょっと突っ込んで考えていきます。この点に関しては、次回お話ししたいと思います。
Selenium
さらに昨今では、Webアプリケーションに対するテストでは、Seleniumというような非常に強力なツールがあります。
この場に『WEB+DB PRESS Vol.36』でSeleniumに関する記事を書かれた宮澤さんにも来ていただいていますので、Seleniumとテスト駆動の関係や、Seleniumについてのご意見をお聞きしてみたいと思います。
宮澤さんのコメント
僕自身は、テスト駆動開発でSeleniumを使えるとはあまり思っていないですね。
ありがとうございます。
Selenium自体は、TDD的な無から有を生むようなアプローチというよりは、回帰テストとして使っていくのが良いのではないかというご意見をいただきました。
回帰テストとは、すでに動いている機能が、今も動いていることを証明するためのテストです。すでに達成されている機能を、これから先もずっと動作させ続けるための、資産としてのテストですね。
たとえば新しい機能追加したことで、既存の回帰テストが失敗したら、新しい機能によって既存のコードに何かバグを入れてしまったということになるわけです。このように、バグを入れてしまったことを検知するためのセーフティネットとして、回帰テストというしくみをプロジェクトに導入することはよくあります。