Developer Test から Customer Test へ #swtestadvent2011

これは、「Software Test & Quality Advent Calendar 2011」の12/15分のエントリーです。ソフトウェアテストや品質に関するAdvent Calendarという事で、前のエントリーは@kz_suzukiさんの「それでもやはり、品質を「予測」したい」になります。

Developer Test と Customer Test

Developer Test と Customer Test とは、和田さんの講演の中で出てくる誰が何のためにテストを行うのかという観点で分類したテストの2つです。書籍「実践アジャイルテスト」の中でもテストの4象限モデルとして、ビジネス面/技術面の軸とチームの支援/製品を批評の軸によるテストの分類が紹介されていますが、ほぼ同じものと考えて良いでしょう。
Developer Testとは、開発者が開発者の為に、開発を行いやすくするためのテストであり、TDD・ユニットテストコンポーネントテストなどが該当します。テストはxUnitなどを使用して自動化され、プログラムの細かい部分をボトムアップでテストします。しかし、ユニットテストはプロダクトの品質を直接高めるテストではありません。結果としてプロダクトの品質は高くなる事はあっても、顧客にとってStringUtilsの数値フォーマットメソッドが正しく機能している事は、通常はビジネス上のメリットとならないからです。
一方、Customer Testとは、名前の通り顧客の視点に立ったテストです。顧客または顧客の役割を代行した開発者が、プロダクトの個々の機能が実現できているかを測るためのテストであり、プロダクト全体の進捗を知るためと考える事もできます。Customer Testは、インテグレーションテスト・ファンクショナルテスト・ストーリーテストなどとして実施され、顧客が理解できる形・内容で、トップダウンでテストします。Customer Testは、Cucumberのような専用のツールを使う場合もあれば、xUnitで行う事もあります。ユースケースシナリオをベースとしたテストなどでは、自動化が難しい場合もあるため、Developer Testよりは自動化の割合は減るでしょう。
尚、ユーザーエクスペリエンステストやユーザビリティテストといった見た目や使い勝手に関するテストは、Customer Testよりは品質に関するテストに近いモノです。見た目や操作感などはプログラムによる自動化テストは困難ですし、GUIのテストは苦労する割に効果が得られにくいものです。したがって手動で行う事が妥当となる事が多いでしょう。

プログラマが学ぶべきテスト

もし、あなたがプログラマであるのであれば、Developer Test から学ぶべきです。Developer Test の中でもxUnitファミリーを使用したユニットテストを学ぶべきでしょう。これは、先日の「TDDを学ぶべき10の理由」でも書いたようにプログラマとしてのスキルアップに繋がるからです。自分の中では開発とは「要求を実現すること」と定義していますが、実現するためには実現するための手段が必要不可欠です。実現する手段という手札を増やし、基礎体力を付けることは開発者として生きていくならば最初に身につけなければならないことです。そして、一生やらなければなりません。
はじめは、計算機やWiki構文変換などの単一の機能で閉じるユーティリティメソッドなどからテストをしてみましょう。入出力に注意し、どんなデータを必要とするのか、どんなテストがどの程度あれば安心できるのかなどを肌で覚えます。それに慣れたならば、少しだけ複雑な機能をテストしてみましょう。Listなどのコンテナ構造、状態を持つクラス、モックやスタブを使う必要のあるクラス、データベースなどフィクスチャが複雑なクラスなどです。このレベルになると、ほとんどのケースでテストはパターン化できます。パターン化し、「あぁ、こんなクラスのテスト書いたことあるわー。3年前に書いたわー」となる事が目標です。
ところが、ユニットテストを学んでいく中で壁を感じる時期を迎えます。なんとなくユニットテストを書く事には慣れてきたのだけど、少し大きいモジュールや機能になると、どこから手をつけていいのか解らなくなるのです。でも、安心してください。それは、次のステップに進む段階に来たという事です。

トップダウンで考える

ユニットテストの対象はクラスやモジュールといったプログラムの最小構成単位です。したがって、着目しなければならない範囲は小さいため、テストに必要な入出力や仕様の把握は比較的に簡単です。しかし、その部品がプロダクトの中でどのような位置づけにあるかについて忘れてしまう事があります。TDDを行った場合、顕著になるでしょう。TDDはボトムアップでプロダクトを構築する開発手法であるため、意識的に全体像を見るようにしなければ、開発の中で迷子になってしまうのです。
つまり、プロダクトをトップダウンで見て開発する事も必要です。ほとんどのシステムでは、なんらかのインターフェイスがあり、顧客やタイマーなどの操作により入力が行われ、なんらかの出力が行われます。このシステムのインターフェイスは、ユーザが認識できるシステムの形になります。内部の細かいモジュールについて顧客は認識できませんが、画面やAPIといった外部インターフェイスは顧客が認識でき、ビジネス的価値をもたらすものです。したがって、外部インターフェイスを考え、外部インターフェイスのテストを書く事ができれば、それは1つの顧客に価値をもたらすゴールの1つとなります。また、そのゴールを目指すことが開発の目標となるのです。そのゴールを達成するために必要の無いクラスやモジュールはムダでしかありません。

ファンクショナルテストを作る

ファンクショナルテスト(機能テスト)は、Customer Testの1つであり、システムを外部から操作した時の振る舞いを検証するテストです。ただし、外部インターフェイスのテストですが、GUIに関するテストを含めてはいけません。システムにおいてGUIは重要な外部インターフェイスです。しかし、美しさ・ユーザビリティなど感覚に頼ったテストを行わなければならない部分でもあります。さらに、GUIは変更される可能性も高く、テストを書くコストも高いのです。
そこで、ビジネスの本質となる部分のテストを考えます。GUIはテストしないという方針を立てたならば、どこまでの処理が本質でテストすると効果的かを見極めます。ウェブアプリケーションであれば、コントローラ層のテストを行わずサービス層のみに集中して行うというのも1つの戦略です。それでは不十分ならば、コントローラ層も含めビューに依存しない形でテストを行う戦略もとれます。例えば、JSONで結果を返すREST的なAPIを別途用意してテストすることもできます。フレームワークのサポートなどがあれば、ビュー(テンプレート)に渡されるモデルについてテストも記述できるでしょう。上手くGUIに依存する部分を省き、エンドトゥエンドのテストを実行する事が目指す所です。ファンクショナルテストでは、どのようにどの程度のテストを行うかというテスト戦略を立てる事が重要です。

テストドライバーを作成する

ビューに依存したテストは書きにくいモノです。例えば、ウェブアプリケーションで、幾つかのフォームに値を入力して「送信」ボタンを押した時のエンドトゥエンドのテストを実現するのは厄介です。そのような時は、テストで実行したい操作を抽象化して定義されたテストドライバーを作成すると上手くいきます。ブラウザ上のボタンをクリックするのではなく、例えば次のようにプログラムとして記述できるフレームワークを作成するのです。

@Rule
WebTestDriver testDriver = new WebTestDriver("localhost", 8080);
@Test
public void 送信のテスト() throws Exceptipon {
    testDriver.loginWith(new Account("admin"));
    testDriver.open("/user/account");
	testDriver.forms.put("age", "30");
	testDriver.forms.put("favorites", "Java");
	testDriver.forms.submit();
	assertThat(testDriver.response, is(containsText("result: OK"));
}

裏側ではどのような事が行われているかというのは解りません。重要なのはテストを書くプログラマが抽象的にウェブアプリケーションの操作を行い、重要な部分について検証を行っているということです。
これでトップダウンのテストを書く事ができました。後はボトムアップで必要なクラスやモジュールを作っていくだけです。TDDを行うと効果的でしょう。

設計

もし、ゴールまでがとても遠い、大きな機能であるならば、もう数回の設計が必要かもしれません。設計とは適切な大きさで分割できるモジュールに分ける作業です。適切なインターフェイスを持つ幾つかのモジュールに分割してください。この時も、インプットとアウトプットに着目します。テストのスキルを高めることで設計力も高まるのです。さあ、各モジュールについてテストを書いてからモジュールを実装しましょう。

まとめ

テストと言っても大きく顧客に価値のあるビジネス面のテストと、開発者が開発を円滑にするための技術面のテストがあり、開発者にとってどちらも習得しなければならないスキルです。我々プログラマユニットテストなどボトムアップのテストに興味を持ちます。それは最初のステップとして重要な事です。ですが、それは開発者自身のためのテストであり、顧客のためのテストではないのです。開発者として顧客の要求を実現するには、顧客のビジネス的価値を検証するためのテストを書く事を意識しなければなりません。これらのボトムアップトップダウンのアプローチを上手く使い分ける事で品質の高いシステムを安心して開発できるようになるでしょう。