コード品質を追求する: TestNG-Abbot による GUI テストの自動化

フィクスチャー・オブジェクトを使って GUI コンポーネントを簡単に検証する

TestNG-Abbot は、GUI コンポーネントのテスト方法に新風を吹き込むテスト用フレームワークです。今月は Andrew Glover が、TestNG-Abbot による GUI テストで最も難解な部分、つまりユーザー・シナリオがどのように展開するかを把握すること、について説明します。これさえ理解してしまえば、このフレームワークの便利なフィクスチャー・オブジェクトを使って GUI コンポーネントを分離して検証するのが、どれほど簡単であるかがわかるはずです。

Andrew Glover, CTO, Stelligent Incorporated

Andrew GloverAndrew Gloverは合衆国ワシントン特別区にある、Vanward TechnologiesのCTO(最高技術責任者)です。Vanward Technologiesは自動化テスト・フレームワークの構築を専門としており、ソフトウェアのバグ発生数や統合時間やテスト時間の減少、また全体的なコード安定性改善に貢献しています。



2007年 2月 27日

Swing や AWT などの類いでビルドされたユーザー・インターフェースは、今まで開発者にとって骨の折れるテスト対象でした。その理由は以下のとおりです。

  • 基礎となるグラフィック・フレームワークが複雑なこと
  • GUI 内で表示とビジネス・ロジックが結合されていること
  • 直観的な自動テスト用のフレームワークがないこと

最初の 2 つはもちろん今に始まったことではありません。グラフィック・フレームワークは本来複雑なもので、ビジネス機能を GUI アプリケーションに追加すると必ずテストの障壁となります。その一方、使い勝手のよいフレームワークはこの数年でいくつも登場していて、実際に GUI のテストを容易にしています。

今月は、GUI テストの苦痛を大幅に和らげてくれる新しいフレームワークを紹介します。

TestNG-Abbot の紹介

TestNG-Abbot は、成功した 2 つの開発者テスト用フレームワーク、Abbot と TestNG の結合によって生まれました。Abbot は GUI コンポーネントをプログラム上で分離できるようにすることを主眼とした JUnit 拡張フレームワークで、GUI の振る舞いを簡単に検証する方法を提供しています。Abbot では例えば、ボタン・コンポーネントへの参照を取得してプログラム上でクリックし、それによって行われるアクションを検証することが可能です。Abbot にはスクリプト・レコーダーも付属しているため、これを使用すれば、XML 形式でテスト・シナリオを設計してプログラム上で実行することもできます。

コード品質の改善をお望みですか?

そうであれば Andrew の「Improve Your Java Code Quality」ディスカッション・フォーラムは見逃せません。このフォーラムでは、コードの評価基準、テスト・フレームワーク、そして品質に重点を置いたコードの作成方法について、生の情報を入手できます。

TestNG については、この連載で十分説明してきたのでコメントを絞りますが、基本的にTestNG は JUnit に代わるものです。TestNG では期待されるすべての機能に加え、追加の機能も実現しています。別の記事に書いたように、TestNG はとりわけ上位レベルのテストに最適で、依存関係を使用してテストを行い、失敗したテストだけを再実行できます。つまり、GUI のテストに役立つテスト・タイプというわけです (TestNGについての詳細は、「参考文献」を参照)。

両方の親がこれだけ優れているだけあって、その子供である TestNG-Abbot は当然、ずば抜けた才能の持ち主です。Abbot と同様、TestNG-Abbot では GUI コンポーネントをプログラム上で分離できます。同時に TestNG のアサーションを使用して、GUI の事細かな操作を、検証メソッドを公開する単純なフィクスチャーに抽象化します。TestNG-Abbot の直観的なフィクスチャー・クラスを正しく使用すれば、GUI テストは赤ん坊からキャンディーを取り上げるくらい簡単になります (赤ん坊からキャンディーを取り上げたいとは思わないでしょうが)。

直観的なフィクスチャー・クラス

TestNG-Abbot の現行リリースは、7 つのフィクスチャー・タイプをサポートします。この 7 つには、ボタン、メニュー、ラベルの操作、そしてテキスト・フィールドなどのテキスト入力コンポーネントの操作を対象としたタイプが含まれます。さらに、フィクスチャー・タイプは名前のみから、テスト対象のコード (つまり、GUI のコンポーネント) に論理的にリンクされます。これによって GUI とそのテストの疎結合が実現するわけですが、この疎結合は少なくとも以下の 2 つの理由で有益です。

  • GUI コンポーネントの具体的な位置に依存しないでテストできるので、テストを中断することなく GUI コンポーネントを移動することができます。
  • テストを早い段階で作成し、その後、開発段階でレイアウトや見た目の変更を行えます。

現在サポートされているのは 7 つのフィクスチャー・タイプのみですが、近日中に他のタイプもサポートされる予定です。フィクスチャー・タイプが増えれば、GUI をプログラム上で検証する上で TestNG-Abbot の有効性が増すことでしょう。


普通とは違う GUI の検証

TestNG-Abbot は GUI の検証プロセスをより簡単にしますが、だからと言ってプロセスが簡単だというわけではありません。GUI のテストには、ユニット・テストやコンポーネント・テストとは違ったアプローチが必要です。GUI 内のビジネス・ルールを検証するプロセスは、ユーザー・シナリオを検証するプロセスになります。別の言い方をすれば、GUI のテストには表示状態の変更が含まれるということです。

例えば、注文入力の GUI で保存ボタンを押すと、ビジネス・ルールでは注文内容がデータベースに保持されると規定しているとします。その一方、ユーザー・シナリオでは成功したというステータス・メッセージをボタンの下に挿入すると規定している場合、この特定のテストを TestNG-Abbot で作成できます。実際には、GUI が上手く設計されていれば、注文内容がデータベースに保持されることをテストするのに GUI をテストする必要はありません。要するに、ビジネス・ルールとユーザー・シナリオの 2 つのテストを同時に早い段階で作成できるということです。

ぜひ試してみてください

TestNG-Abbot ではエンド・ツー・エンドのテストを禁止しているわけではないという点をお忘れなく。TestNG-Abbot を DbUnit と簡単に組み合わせて、例えばユーザー・シナリオとビジネス・ルールの両方を検証する反復可能なテストを作成することができます。


Word Finder の GUI

TestNG-Abbot の機能を説明したところで、1 つの動作をする単純な GUI を作ってみました。この GUI は、指定された単語を基礎となる辞書 (データベースになっています) で検索し、その単語の定義を表示するものです。アプリケーションが実際にどのようにコーディングされているかに関わらず、この GUI のユーザー・シナリオには以下の 3 つのステップが伴います。

  1. テキスト・ボックスに単語を入力する。
  2. Find Word ボタンをクリックする。
  3. 定義の有無を確認する。

もちろん、上記以外のケースもあります。例えば、ユーザーが単語を入力せずに Find Word ボタンをクリックしたり、無効な単語を入力した場合などです。そのようなシナリオを扱う方法については、テスト・ケースをいくつか追加して説明します。

GUI について

図 1 に、Word Finder の起動時の GUI を示します。GUI がこのように単純な理由は、TestNG-Abbot の 3 つのフィクスチャー・クラスと、いくつかのテスト・ケースを説明するためだということを念頭に置いてください。

図 1. Word Finder の GUI
図 1. Word Finder の GUI

TestNG-Abbot でテストする際には、まず GUI の各構成部分について検討する必要があります。Word Finder GUI は、図 2 に示す 3 つのコンポーネントで構成されています。

図 2. Word Finder GUI のコンポーネント
図 2. Word Finder GUI のコンポーネント

ご覧のように、Word Finder GUI は、検索対象の単語を入力する JTextField、GUI に辞書データベースから単語の定義を取得させるための JButton、そして実際の単語の定義が表示される JEditorPane で構成されています。

期待どおりのシナリオでは、pugnacious (けんか腰) と入力して Find Word ボタンをクリックすると、図 3 に示すように JEditorPane に「Combative in nature; belligerent (闘争的な気質、攻撃的)」と表示されます。

図 3. 期待どおりのシナリオ (すべて順調に機能します)
図 3. 期待どおりのシナリオ (すべて順調に機能します)

TestNG-Abbot によるテスト

TestNG-Abbot ではまず始めに、TestNG の BeforeMethod および AfterMethod アノテーションを使って GUI のインスタンスを作成するための標準的なテスト・フィクスチャーを作る必要があります。TestNG-Abbot フレームワークには便利な AbbotFixture オブジェクトがあり、このオブジェクトを使用することによって、GUI コンポーネントの操作を容易にし、テスト・プロセス全体を立ち上げて基本的に使用できる状態にすることができます。このオブジェクトをテスト・フィクスチャーに組み込むには、テストの前に GUI のインスタンスをフィクスチャー・オブジェクトの showWindow() メソッドに渡し、それから適切な名前が付けられている cleanUp() メソッドでフィクスチャーにクリーンアップを実行させます。

リスト 1 は、私が作成した TestNG テスト (実際にはテストは含まれていません) です。このテストでは、フィクスチャーで TestNG-Abbot の AbbotFixture オブジェクトを使用して Word Finder GUI のインスタンスを保持しています。

リスト 1. AbbotFixture オブジェクトを使用した WordFindGUITest の定義
public class WordFindGUITest {
 private AbbotFixture fixture;

 @BeforeMethod
 private void initializeGUI() {
  fixture = new AbbotFixture();
  fixture.showWindow(new WordFind(), new Dimension(269, 184));
 }

 @AfterMethod
 public void tearDownGUI() {
  fixture.cleanUp();
 }
}

ユーザーから見える Word Finder GUI の振る舞いは、図 2 で説明した 3 つのコンポーネントに影響を及ぼすため、これらのコンポーネントをプログラム上で微調整し、期待どおりの結果が得られるようにしなければなりません。例えば、図 3 の期待どおりのシナリオを検証するには以下の 3 つのステップが必要です。

  1. JTextField への参照を取得し、その参照にテキストを追加する。
  2. JButton のハンドルを取得してクリックする。
  3. JLabel コンポーネントへの参照を取得し、正しい定義が存在することを確認する。

期待どおりのシナリオを検証する

TestNG-Abbot では、上記の 3 つのステップごとにそれぞれ便利なフィクスチャー・タイプを使用できます。この 3 つのフィクスチャー・タイプは、JTextField 用の TextComponentFixture、Find Word ボタン用の ButtonFixture、そして JLabel に含まれる特定のテキストを確認するための LabelFixture です。

図 3 に示した結果になることを検証するためのコードは、リスト 2 のようになります。

リスト 2. 期待どおりのシナリオのテスト
@Test
public void assertDefinitionPresent() {
 TextComponentFixture text1 = new TextComponentFixture(this.fixture,
   "wordValue");
 text1.enterText("pugnacious");

 ButtonFixture bfix = new ButtonFixture(this.fixture, "findWord");
 bfix.click();

 LabelFixture fix = new LabelFixture(this.fixture, "definition");
 fix.shouldHaveThisText("Combative in nature; belligerent.");
}

フィクスチャー・オブジェクトが論理名を使って、特定の GUI コンポーネントに結びつけられていることに注目してください。例えば Word Finder GUI では、JButton オブジェクトを「findWord」という名前にプログラム上で結びつけています。リスト 3 に示すように、ボタンを定義する際にコンポーネントの setName() メソッドを呼び出すことによって、いかにしてオブジェクトと名前を結びつけることができているかに着目してください。

リスト 3. Find Word ボタンの定義
findWordButton = new JButton();
findWordButton.setBounds(new Rectangle(71, 113, 105, 29));
findWordButton.setText("Find Word");
findWordButton.setName("findWord");

リスト 2 で注目すべきもう 1 つの点は、ボタンへの参照を取得するために、「findWord」という名前を TestNG-Abbot の ButtonFixture オブジェクトに渡していることです。このボタンを「クリック」できるようにした (click メソッドの呼び出しで) 方法はとても賢いやり方で、その後で TestNG-Abbot の LabelFixture オブジェクトを使って定義が存在することを表明しています。これは自己満足でも何でもありません。


予期せぬ事態のテスト

当然のことながら、Word Finder GUI を本気で検証しようと思ったら、予期しないことをユーザーが行った場合にもすべてが機能することを確実にしなければなりません。予期しないこととは、ユーザーが単語を入力する前に Find Word ボタンを押したり、あるいはそれよりも厄介な事態として、ユーザーが無効な単語を入力したりすることです。例えば、ユーザーがテキスト・フィールドを空白のままにすると、GUI はリスト 4 に示す特定のメッセージを表示することになっています。

図 4. 予想される例外ケース
図 4. 予想される例外ケース

このようなケースも当然、TestNG-Abbot で簡単にテストできるはずです。必要な操作と言えば、空白の値を TextComponentFixture に渡して (ButtonFixture で click メソッドを使用して) ボタンをクリックし、「Please enter a valid word」という極めて簡潔な応答を表示するだけです。

リスト 4. 例外ケースのテスト: 単語を入力しないでボタンをクリックした場合
@Test
public void assertNoWordPresentInvalidText() {
 TextComponentFixture text1 = new TextComponentFixture(this.fixture,
   "wordValue");
 text1.enterText("");

 ButtonFixture bfix = new ButtonFixture(this.fixture, "findWord");
 bfix.click();

 LabelFixture fix = new LabelFixture(this.fixture, "definition");
 fix.shouldHaveThisText("Please enter a valid word");
}

リスト 4 で明らかなように、目的の GUI コンポーネントへの参照を取得するコツさえわかれば、後はそれほど難しいことではありません。最後のステップは、予想されるもう 1 つの例外ケースとして、無効な単語が入力された場合を検証することです。そのための手順は、リスト 1リスト 3 の場合と非常によく似ていて、目的の String を TextComponentFixture オブジェクトに渡してクリックし、特定のテキストの有無を表示するだけです。リスト 5 では、この一連の操作がすべて行われています。

リスト 5. 簡単に検証できるもう 1 つの例外ケース
@Test
public void assertNoWordPresentInvalidText() {
 TextComponentFixture text1 = new TextComponentFixture(this.fixture,
   "wordValue");
 text1.enterText("Ha77");

 ButtonFixture bfix = new ButtonFixture(this.fixture, "findWord");
 bfix.click();

 LabelFixture fix = new LabelFixture(this.fixture, "definition");
 fix.shouldHaveThisText("Word doesn't exist in dictionary");
}

リスト 5 は、図 5 に示す機能を検証する方法として上出来だと思いませんか。

図 5. 無効な単語の入力
図 5. 無効な単語の入力

まんざらでもありません。TestNG-Abbot では 3 つの異なるユーザー・シナリオを難なくテストできました。それぞれのケースに必要だったのは、テスト対象のコンポーネントの論理名と、シナリオを作成するための一連のステップだけです。


早速 GUI をテストしてみてください

TestNG-Abbot はテスト方法としては新顔かもしれませんが、その前駆者から極めて有用な機能を引き継いでいます。この記事では、TestNG-Abbot を使用してプログラム上で GUI コンポーネントを分離し、フィクスチャーによってコンポーネントの検証メソッドを公開する方法を説明しました。このプロセスから、すべてが論理的に正しく機能する期待どおりのシナリオと、任意のユーザーが関わってくると当然考えられる例外的なシナリオの両方を、いかに簡単にテストできるかがわかったはずです。いずれの場合にしても、テストする上で必要なのは、シナリオを理解し、そのシナリオで役を演じるコンポーネントを理解することだけです。TestNG-Abbot の便利なフィクスチャー・オブジェクトを使用すれば、コンポーネントの振る舞いを簡単に変更することができます。

参考文献

学ぶために

製品や技術を入手するために

  • TestNG のダウンロード: JUnit と NUnit に端を発していますが、新しい機能を導入して一層強力で使いやすくなったテスト・フレームワークです。
  • TestNG-Abbot のダウンロード: TestNG と Abbot フレームワークの結合によって、今までになく簡単に GUI をテストできます。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=250250
ArticleTitle=コード品質を追求する: TestNG-Abbot による GUI テストの自動化
publish-date=02272007