T.MURACHIの日記: GUI テスト考
そもそも GUI のアプリを作るのにユースケース定義書なんて物をしたためること自体が間違いなのだとは思うのですが。
例え話なのですが、まず前提条件として、作っているアプリケーションというのは、ある形式のファイルを読み込ませると、そのなかのデータを用いて、ビュー上 (ウィンドウ内) に特定のグラフィックを表示し、それに対して特定の操作を行なうことによって、さまざまな形態でそのグラフィックの表示を変化させられるようなものであると想定しましょう。ちょっと抽象的な表現ですが、具体例を上げれば、例えば HTML ファイルを読み込んで表示する Web ブラウザであるとか、PostScript や SVG のようなベクタ情報 (ドロー系グラフィック) のファイルを読み込んで表示するグラフィックビューアであるとか。前者ならばスクロールバーを上下に動かしてスクロールさせたり、スタイルシートを差し替えて見栄えを切り替える、なんて操作が考えられますし、後者であれば部分拡大や色彩の変更、などといった操作が考えられるでしょう。
さて、そんなアプリケーションに今、一つのメニューを追加するとします。以下にそのメニューの外部仕様例をさらっと書いて見ましょう。
- そのメニューを選択すると、モードレスダイアログが 1 つ表示される。
- そのダイアログが表示されている間は、本メニューを含む一切のメニューを選択することはできない。また、ビューに対して何らかのマウス操作を行なうこともできない。
- そのダイアログは、以下に列挙するコントロールによって構成されている。
- "モード A" と "モード B" の 2 つのラジオボタン
- "OK" ボタン
- 2 つのラジオボタンは 2 つ合わせて 1 セットであり、どちらか一方のみを選択可能。また、必ずどちらかが選択された状態となっている。ダイアログ表示直後のデフォルトでは "モード A" が選択された状態となる。
- 本機能は既に読み込まれているデータを、2 つのモード "モード A" と "モード B" による然るべき方法によって切り替えて表示を行なうものである。すなわち、ラジオボタンの "モード A" が選択されている間は "モード A" としての、"モード B" が選択されている間は "モード B" としての方法により、ビュー上でのグラフィック表示が行なわれる。
- 本ダイアログが表示されている間、2 つのラジオボタンは何度でも選択しなおすことができる。選択が切り替わるたびに、ビュー上のグラフィックは追従して表示を切り替えること。
- "OK" ボタンをクリックするとこのダイアログは閉じられ、非表示となる。
- 本ダイアログが閉じられると、ビュー上の表示はメニューを選択する前の状態に戻る。また、メニューはすべて元通り選択できるようになり、ビュー上でのマウス操作も可能となる。
この例え話は実話に基づいて作っています。従って、実際にはこの抽象的に表現された仕様の、より具体的なものが、私の職場には存在していることになるわけですが、まぁ多分、このぐらい抽象的に書いておけば、関係者以外は何のことやらさっぱり、で済むことでしょう (^^)。
んで、この外部仕様を受けて、下請けの会社の人が作ってきてくれたユースケース定義というのがあって、それの抽象的な作成例が以下のようなものだったりするわけです。
- パス 1 — メニュー XX を選択し、グラフィックを "モード A" で表示し、"OK" ボタンで終了する方法
事前条件: ファイルが読み込まれている。- ユーザーは、メニュー XX を選択する。
- システムは、「XX」ダイアログを表示する。
- システムは、すべてのメニューを無効にし、選択できなくする。また、ビュー上でのマウス操作も一切行なえないようにする。
- システムは、「XX」ダイアログの "モード A" ラジオボタンを選択状態にする。
- システムは、ビュー上にてグラフィックを、"モード A" による方法を用いて表示する。
- ユーザーは、「XX」ダイアログの "OK" ボタンを押す。
- システムは、「XX」ダイアログを非表示にする。
- システムは、ビュー上の表示を、1. でメニューを選択する前の状態に戻す。
- システムは、すべてのメニューを元通り選択可能な状態に戻す。また、ビュー上でのマウス操作もできるようにする。
事後条件: 1 で操作を開始する前の状態に戻り、変化なし。
- ユーザーは、メニュー XX を選択する。
- パス 2 — メニュー XX を選択し、グラフィックを "モード B" で表示し、"OK" ボタンで終了する方法
事前条件: パス 1 と同じ。- パス 1 の 1. と同じ。
- ユーザーは、「XX」ダイアログの "モード B" ラジオボタンを選択する。
- システムは、ビュー上にてグラフィックを、"モード B" による方法を用いて表示する。
- パス 1 の 2. と同じ。
事後条件: パス 1 と同じ。
さて、このユースケース定義に基づいて、開発およびテストは進行してゆくものとします (事実、職場の現下請けはそうやってシゴトを進めているのであったりします)。開発の方はまぁ、とりあえずおいておくとして、ここではテストの方に着目してみることにしましょう。
開発と平行して、テスト仕様作成の担当者が、上記の UC を元に、以下のようなテストチャートを作ってくれたとします。ただし、こちらに関しては、以下に記述するような抽象的なテスト仕様にそうとうするものは、現実にはまだ出来上がっていなかったりしますので、実際にはこれよりマシなものができあがるかもしれないし、そうであることを願って止まなかったりもします。でも多分こんな感じになるんだろうなぁなどと、8 割がた諦めていたりもしています。
- XX メニューを選択する。
- 「XX」ダイアログが表示される。
- すべてのメニューが無効になり、選択できなくなる。
- ビュー上でマウス操作ができなくなる。
- ラジオボタンは "モード A" が選択されている。
- ビュー上では "モード A" による方法により、グラフィックが表示される。
- 「XX」ダイアログの "OK" ボタンを押す。
- 「XX」ダイアログが非表示になる。
- ビュー上の表示は 1. の前の状態に戻る。
- すべてのメニューが 1. の前のときと同じように選択できるようになる。
- ビュー上でのマウス操作が可能になる。
- XX メニューを選択する。
- 1. と同じ。
- 「XX」ダイアログの "モード B" ラジオボタンを選択する。
- ビュー上では "モード B" による方法により、グラフィックが表示される。
- 「XX」ダイアログの "OK" ボタンを押す。
- 2. と同じ。
突っ込みどころは多々あることでしょう。例えば、ビュー上でのマウス操作ができないことを証明するために、何を行なう必要があるのかが明記されていない、とか。あるいはそもそも一続きの操作手順と動作例だけでテストしようとしてしまってよいのか? といった議論もあるかもしれません。
しかしそのような突っ込みは不安定要素ではあるものの、ここでは一切目を瞑ることとしましょう。それでも、それにつけてもやはりこのテストチャートは、テストとして不十分であるということが言えてしまいます。何故ならこのテストチャートでは、必要と思われる以下のことが、確実にテストされないことが分かっているからです。
- "モード B" が選択されている状態で "モード A" を選択したときに、ビュー上の表示が "モード A" で正しく表示されること。
- 既に選択されている方のラジオボタンをクリックしても、ビュー上の表示は切り替わらないこと。
- "モード B" を選択した状態でダイアログを閉じた後、再びこのダイアログを開いたときに、ダイアログ上のラジオボタンは "モード A" が選択され、ビュー上の表示も "モード A" による方法で正しく表示されること (「ダイアログ表示直後のデフォルト」が守られていることの確認)。
- "OK" ボタン以外による方法 (Windows なら、[ESC] キーや、右上の「×」ボタンなど) でダイアログを閉じたときに、ビューの表示やメニューの状態が元に戻り、ビュー上でのマウス操作も可能になること。
1. は、"モード A" → "モード B" のケースは含まれているにもかかわらず、その逆のケースが含まれていないというもので、バリエーションの展開不足であると言えます。プログラミングの観点から見ればとんでもない「うっかりミス」のレベルではありますが、仮に "モード A" ラジオボタンがクリックされたときの処理にバグがあれば、致命的な不具合が残ることになります。
2. は、どちらのラジオボタンをクリックしても同じハンドラ関数が呼ばれるようなライブラリを使っていて、引数に渡される値などで処理を切り分ける、といった仕組みである場合に、タコプログラマーが「ハンドラが呼ばれるたびに無条件でビュー上の表示を切り替える」といった間違った実装をした場合には問題になります。ありえないと思う人はマーフィー学をおさらいした方がよいです。「誰でも使えるものは作れない。なぜなら、バカは思いもよらないことを考え出すからだ。」
3. は、外部仕様でちょろっとかかれていることが期待通りに作られているかという問題に過ぎず、これが期待通りでなかったとしても、実用上はそれほど問題にはならないでしょう。
4. は、このアプリケーションが動作する環境や開発環境にも依存する問題ですが、"OK" ボタンをクリックした場合と、それ以外の場合とで、送られるメッセージが異なり、呼ばれるハンドラが違ってくる場合には問題になります。実装面でも一番見落とされやすそうな個所であり、その上バグがあった場合にはそれ以降の一切の操作が出来なくなってしまう可能性をはらむ極めて深刻な不具合となりえます。
これら以外にも私自身、いくつか見落としているケースがあるかもしれませんし、また極端なケースはいくらでも考えられるでしょう。もっとも、極端なケースについては恐らく内部テスト (モジュール単体テスト等) や開発管理でカバーできる可能性も大きいので、ここではそれほど気にする必要は無いかもしれません。
問題は、これらの見落としは、テストチャートがユースケースのみに頼り切って作られていることに由来している、ということです。つまり、ユースケースとして例示されているパターンのみを網羅し、それで満足してしまっていることなのです。
この問題に対する解決のアプローチは、大きく分けて 2 通りあります。1 つは、テスト作成者がユースケースの記述と外部仕様の両方を参考に、バリエーションを広げるよう注意すること。もう 1 つは、ユースケース定義自体を行なわず、外部仕様のみに基づいてテスト仕様を作成することです。
前者は難しいかもしれません。なぜなら、既に固定化された操作手順を解体して、パターンを割り出すという作業は、よほど経験を積んだ人か、あるいはよほど機転の利く切れ者でもない限り、完全には不可能だと思われるからです。至るところで見落としは出てくるでしょうし、逆に拡大解釈しすぎて無駄なテストを増やすことにもなりかねません。
後者は賛否両論分かれるところだと思いますが、私は先に例示したようなテストチャートという形態にこだわらないのであれば、こちらの方がよほど現実的であると思っています。大まかな操作の流れをユースケースとして例示することによって、開発やテストに方向性を持たせ、作業性を向上させる、というのが、おそらくユースケース定義の存在意義なのだと思いますが、そもそもそのような方向性を持たせてしまうのは正しいことなのでしょうか? 開発者は、ユーザーがそれをどのように使うのかを規定して物を作るのではなく、あるものを作ったときに、ユーザーはそれをどのように使う可能性があるのかを想定して開発を行うべきなのではないでしょうか?
もちろん、理想ばかりをいっていても仕方の無いところはあります。それこそ、いくら想像し、安全性を広げていってみたところで、「バカは思いもよらないことを考え出す」現実に、完全に打ち勝つことはできないかもしれません。それでも、十分な広がりに対して効率的に問題の要点を絞り込むための知恵、数々の手法が、懸命なる先人たちの手によって培われてきたはずです。オブジェクト指向なんてのはその最たるものではないでしょうか? メニュー、ダイアログ、ダイアログ上のコントロール、ビュー、ビュー上でのマウス操作による動作、ダイアログでの操作とビューにおける表示との連動、そういったもののすべてを、「操作手順」ではなく、「部品」として捉えることができたならば、要点はもっともっと簡単に絞り込めるはずだし、それに伴ってテストだってもっと簡単に、しかも確実に品質の高いものを作り上げることができるはずなんです。C++ や Java を使えさえいればオブジェクト指向だなんて思っているならば、それは根本的に間違いですので考えを改めましょう。言語マニアな人はたくさんいるけど、その辺が理解できてる人って、あんまりいないんじゃないかなぁとか思っちゃう今日この頃なのです。。。
GUI テスト考 More ログイン