テストの考え方 - XPとかAgileなんてほとんど聞いたことがない方へ

注意: 不備やいい加減な点などがあるかもしれませんので、見つけましたらご指摘ください。

自動テスト

テストは大抵、仕様を満たしているかどうかを確認するために行われます。テストを行う方法には大きく二つに分けられます。ひとつはいちいち手でシステムを動かして、仕様どおりに動いているのかを目で確認する、という方法。もうひとつはテストの前提条件と実行結果を何かに記述し自動的にテストを行う方法です。


テストは自動的に行えるほうが良いです。なぜなら何度もテストしようとしても、人間の手はミスをすることがあるし、人間の目は間違いを見逃すことがあるからです。


問題は自動テストを行うための準備にかかるコストです。これが高すぎると「手でやった方が早い」ということになってしまいます。逆に言えば簡単にテストが記述できるのであれば、繰り返し同じテストを行える自動テストは非常に便利なものであると言えます。


テストと仕様の関係

さて、テストというと開発の終盤に掛けて行うもの、というイメージが強いかもしれません。しかし、よく考えてください。テストは仕様を満たしているかどうかを確かめるもので、どの道作成するのであれば、仕様が確定した時点で作成してもいいはずです。


もっと推し進めて考えると、誰も読まないような仕様書として仕様を記述しておくよりも、テストとして記述しておいたら非常に価値があるものだとは思いませんか?なぜなら仕様書は実行できませんし、システムの動作が正しいかどうかを判断することもできません。しかし、自動テストはそれが可能なのです。


テストの記述が非常に簡単になったら(将来きっとそうなると僕は思っていますが)、仕様書ではなくテストを書くことが開発の最初の作業になり、きっとその頃には「仕様=テストコード」となっているのではないでしょうか。多分、テストコードからお客様向けのドキュメントを(JavaDocのように?)捻り出すような仕組みが生まれると思います。


テストの分類

ソフトウェア開発におけるテストはよく以下のような分類をされます。
単体テスト
結合テスト
・総合テスト
この分類はオブジェクト指向云々は関係ありません。これらはどのようなタイミングで作られ、使われるのかをお話します。*1

単体テスト

ソフトウェアの部品ごとに行うテストです。XPではTesting Frameworkを使って、対象となる部品がどのように振舞うべきかをTestCaseとして記述します。

基本的な作業の進め方

(1)TestCaseの作成。部品を作り始める前にTestCaseを作成し、どのようにテスト対象が振舞うべきかを記述する。

(2)テストに必要なメソッドなどのインタフェースだけをテスト対象に実装し、メソッドの中身は書きません。これでコンパイルは通るはずです。

(3)テストが失敗することを確認。

(4)テスト対象のコードを記述。

(5)次の仕様を満たすべく(1)に戻る。

誰のためのテスト?

単体テストを作成するのはプログラマです。実行するのもプログラマです。つまり単体テストプログラマのためのテストと言えるでしょう。だから厳密に言うと「仕様どおりに動いているかどうかを確認すること」が目的ではなく「プログラマの思ったとおりに動いているかどうかを確認すること」が目的です。単体テストではプログラマが間違った理解をした場合にそれを指摘することはできません。

設計と単体テスト

単体テストを行う場合に、設計によってはテストがしにくくなる場合もあります。この場合テストの方法を模索するのではなく、設計を変えることを僕はお勧めします。設計はシステムを作る指針を示してくれるだけで、正しくシステムを作れるかどうかを保障してくれません。設計自身が間違っているかもしれないからです。


単体テストは部分的ではありますが、動作が正しいかどうかを教えてくれます。高い品質のシステムを作ろうとするのであれば、どちらを優先するべきかは自明だと思います。*2

テストの重要性

XPの創始者Kent Beckは「テストのないコードは存在しないのと同じ」とまで言います。実際にはGUIなどのテストは非常に難しいため、彼の意見は非常に極端だと言われますが、重要な部品ほどテストが必要だということには誰もが賛成すると思います。


現実的なテストの方法として、GUIにはあまりコードを書かない、という方法があります。テストが難しいものにはテストが必要なコードを書かない、と言い換えることもできますね。


結合テスト

これはXPでは継続的インテグレーションに当たると思います。XPでは当たり前すぎていちいちテストとは呼びませんが、ちゃんと設計されていないシステム開発においては、非常に重要なテストです。

設計はひとつのコミュニケーション

XPのチームでは設計を皆が理解し、誰がどのコードを変更して、それが誰の作業に影響を及ぼすかを考慮するらしいです。これがチームワークと言うべきものなのでしょう。

チームワークのとれていないチームでは、開発者が共有する部分を減らそうとします。たとえば開発者が共有するのはDBだけで、画面ごとに同じようなコードを記述する、とか。


後者の方法は確かにコミュニケーションをとる必要が少なく、人付き合いが苦手な人には向いているかもしれませんが、大きなデメリットがあります。それは同じようなコードが重複して存在しうる、ということです。


同じようなコードが重複して存在すると、仕様が変更されたときに複数のコードに同じような変更を行う必要が生じうることになります。それぞれについてテストもしなければなりませんし、何より人の手で同じ変更を行うことはミスが起こる可能性も生み、テスト自身を間違えるかもしれません。


時間とお金に余裕があってチームワークよりも個人で作業している方が重要、という場合には後者の方法も選択肢としてはアリですが、現実的ではありません。ではどうするべきか?前者のようになりましょう。


設計を練ることによって重複したコードを除去しましょう。設計はDBだけについてではなく、システムの振る舞いについて行うべきものです。その振る舞いを実現する作業のひとつとしてDBの設計を行いましょう。


また、設計は設計者だけのものではありません。理想はチームの全員が同じように設計に関わって設計を効率よく作成することですが、そううまく行かないことも多いので、主に設計者が作成します。


プログラマはそれに従うのではなく、それで自分の作業がうまく進むかどうかをよく考えて、設計者とよく話し合うことが重要です。設計者もプログラマに自分の設計を実装してもらうわけですからプログラマの話をよく聞かなければなりません。つまりコミュニケーションをちゃんとするってことです。


さて、このようにテストが効率よく進むように考慮された設計は、複数の開発者でコードが共有(共同使用)されることになります。すると、誰かが何かを直すと他の人の作業に影響を及ぼすことになるかもしれません。CVSあるいはSVN、VSSなどのソースコード管理ツールから最新版を取得したらコンパイルできなくなった、TestCaseが通らなくなった、などなど。


前振りが長くなりましたが、このようなことがあった場合に異常を知らせてくれるのが結合テストです。

誰のためのテスト?

このテストはチームのためのテストと言えるでしょう。このテストも顧客の要求を満たしているかどうかは教えてはくれません。

実際どう行うか

これは実際のところ単体テストの集まりです。単体テストCVSなどで管理・共有されれば、テスト対象のコードと単体テスト自身の最新版を取得し、全ての単体テストを実行するだけです。


この作業は機械的な作業ですので、いくつかのツールを組み合わせることで全自動で行うことが可能です。この作業と実際のシステムのビルドをあわせて、XPでは「継続的インテグレーション」と呼ぶようです。

設計と結合テスト

結合テスト自身は結局単体テストの集まりでしかないので、どっちが優位か、というのは単体テストについて述べたとおりです。しかし、前述したとおり、結合テストという視点から見ると、テストと設計の関わりが単体テストのそれとは少し違ったものであることに気づきます。


結合テストはチームのコミュニケーションをテストしていると言えるかもしれません。コミュニケーションは口頭や文書だけではなく、CVSなどのツールを介して行う場合もあります。頻繁にソースコードをcommitすれば、それだけ早く間違いが見つかるかもしれません。これも一種のコミュニケーションだと思います。

総合テスト

これは、前の二つのテストと違って、開発者ではなく利用者、あるいは顧客が行うテストです。実際には顧客の代わりにテスターが行うことが多いと思いますが、本質的には顧客が行うものです。


顧客は自分の要求がシステムに思った通りに反映されているかどうかをこのテストを通じて確認します。


XPではこのテストを「受け入れてスト」と呼び、自動化することを勧めます。この受け入れテストのためのFrameworkとしてFIT(http://fit.c2.com/)などがありますが、僕もちゃんと使ったことがありませんので、今のところは地道に手で何回も行うしか能がありません。すみません、そんな奴なのにこんなん書いちゃって。


最後に

XPの話をだいぶ引用しましたが、XPが正しいとか、XPを行うべきだ、と言うつもりはありません。ただXPの中でテストについての技法は非XPの開発プロセスでも重要な示唆を与えてくれると思い、今回の話を書きました。


XPの言葉で"Embrace Change(変化ヲ抱擁セヨ)"という言葉があります。ソフトウェア開発技術は常に進化しています、顧客の要求も常に変化する可能性を持ち、それに伴いシステムのソースコードも変化する可能性があります。その度に「うわ、また変わった」と驚いたり、嘆いてもどうにもならない、変化に付き合うしかないと諦めにも似た気持ちで仕事をするのは、非常に辛いと思います。


だったら「変化しないものなんてないんだ」と割り切ってみたらどうでしょうか。変化を受け入れずに「仕事は辛いもの」という思い込みを実感することに価値を見出せるのであれば、無理にお勧めはしませんが。


「変化を受け入れる」と言うと「どうせ変わるかもしれないんだから大体でいいや」という投げやりな気持ちを想像する人もいるかもしれませんが、「変わるかもしれないけど、ちゃんと作っておかないとどう変わったかも分からないじゃないか」という気持ちと言えば、分かって頂けるでしょうか?


参考
[@IT]-[IT Architect]
http://www.atmarkit.co.jp/farc/index.html
開発プロセス再入門」、「快適なXPドライビングのすすめ」などの記事があります。


オブジェクト倶楽部
http://www.objectclub.jp

*1: 総合テストと結合テストの間に「システムテスト」を行う場合もあるようですが、開発者側が行う最後のテストと思ってください。今回は割愛します。

*2:単体テストが間違っている場合もあります。しかしそれはプログラマの理解が間違っているからで、設計者とよく話をしてどう振舞うべきかを確認すれば、単体テストを直すことになると思います。