SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

HttpUnitを利用したWeb画面テストの自動化

WebブラウザのUIテストを自動で行い省力化する

  • X ポスト
  • このエントリーをはてなブックマークに追加

HttpUnitの動作コード

図2 Eclipse上でのプロジェクト概要
図2 Eclipse上でのプロジェクト概要

LoginTest#setUp()

 サーバーとのやり取りを管理するクラスWebConversationを初期化します。各テストメソッドで使用するため、このメソッドで一度だけ行うようにします。

LoginTest.java(一部抜粋)
import com.meterware.httpunit.WebRequest; 
import com.meterware.httpunit.WebResponse;

public class LoginTest extends TestCase {
    
    WebConversation wc;

    protected void setUp() throws Exception {
        super.setUp();
        
        wc = new WebConversation();
    }

LoginTest#testErrorID()

 ログイン画面で、ログインIDを誤って入力した場合のテストケースです。

LoginTest.java(一部抜粋)
/**
 * 存在しない ログイン ID の入力
 * 
 * @throws MalformedURLException
 * @throws IOException
 * @throws SAXException
 */
public void testErrorID() throws MalformedURLException,
    IOException, SAXException {
    // (1)
    // 初期画面表示
    // ------------------------------------------------------------
    WebRequest request = new GetMethodWebRequest(
        "http://localhost:8080/httpunitapp/login.jsp");
    WebResponse response = wc.getResponse(request);
    
    // ID 入力値
    String wrongID = "aaa";
    
    // (2)
    // 入力フォーム
    WebForm form = response.getForms()[0];
    form.setParameter("loginID", wrongID);

    // (3)
    // 送信実行
    form.submit();
    
    // 遷移後の画面
    // ------------------------------------------------------------
    response = wc.getCurrentPage();
    //System.out.println(response.getText());
    
    // テスト実施

    // (4)
    // 遷移画面 ID
    String sid = response.getHeaderField("X-CZ-SID");
    assertEquals(sid, "SCZ001");
    
    // (5)
    // メッセージコード
    HTMLElement elem = response.getElementWithID("msgCode");
    String msgCode = elem.getText(); 
    assertEquals(msgCode, "2");
    
    // (6)
    // 前回入力した値が再表示されていること
    form = response.getForms()[0];
    String loginID = form.getParameterValue("loginID");
    assertEquals(loginID, wrongID);
}

 まずはログイン画面を起動する必要があります。HTTP GETをエミュレートするためにGetMethodWebRequestクラスをインスタンス化し、サーバーへのリクエストオブジェクトを作成します。そして、そのオブジェクトを使用して、実際にサーバーへの要求を行います。setUp()で初期化したオブジェクトwcを使用したwc.getResponse(request)がそれに当たります。

 実行結果として、レスポンスを表すWebResponseオブジェクトが返ってきます(1)。このオブジェクトを操作して、あたかもブラウザを実際に操作しているかのように、画面内のフォームへと値を設定していきます。

 まず、画面の入力フォームを特定します。

 WebResponse#getForms()でフォームを取得できますが、HTMLでは複数のフォームが定義できるため、配列で返ってくるようなインターフェイスとなっています。今回は入力フォームが1つのみなので、配列位置は「0」としてフォームオブジェクトを取得します。そして、画面で入力するログインIDをWebForm#setParameter()で設定します(2)

 その後、WebForm#submit()メソッドを実行すると、通常のHTMLでのsubmitボタンを押下した時と同様の動作となり、サーバーへリクエストが送信されます(3)

 リクエスト送信後、今度はその結果に応じた画面を操作します。

 WebConversation#getCurrentPage()で、遷移後の画面の内容が取得できます。先のgetResponse()と同様にWebResponseのオブジェクトが返ってくるので、同様にHTML画面内を操作できます。

 このメソッドでは、誤ったログインIDを入力しているので、以下のテスト項目を実施します。

  1. ログイン完了画面ではなく、ログイン画面に遷移している
  2. ログインIDのエラーなので、メッセージコードは「2」となっている
  3. 先ほど入力した誤ったIDがテキストボックスに表示されている

 まずa.ですが、画面識別を行うためのIDはレスポンスヘッダに入れる仕様にしているので、WebResponse#getHeaderField()でヘッダフィールド名を指定し、その値を取得します。それをJUnitのメソッドで評価します(4)

 次にb.ですが、メッセージコードの値を格納するタグはmsgCodeというHTMLのタグidでJSPに記述しています(メッセージのテキストは画面に表示しますが、メッセージコードは表示しません)。

 JavaScriptを記述する際、getElementByIdという関数を使う機会があるかもしれません。これと同様の動作をするメソッドとして、WebResponse#getElementWithID()があり、idの値がmsgCodeのタグを特定できます。その上でHTMLElement#getText()を使用し、タグの中の値を取得して評価します(5)

 最後のc.です。フォームのテキストボックス入力値をチェックします。WebForm#getParameterValue()のパラメータとしてinputタグのname属性の値を指定することにより、そのタグのvalue属性値を得ることができます。先の(2)で入力した値と一致するかを評価します。(6)

LoginTest#testErrorPWD()

 このメソッドでは、入力したログインIDに対して、パスワードが誤っている場合のテストケースを実行します。

LoginTest.java(一部抜粋)
/**
 * 誤ったパスワードの入力
 * 
 * @throws MalformedURLException
 * @throws IOException
 * @throws SAXException
 */
public void testErrorPWD() throws MalformedURLException,
    IOException, SAXException {
    // 初期画面表示
    // ------------------------------------------------------------
    WebRequest request = new GetMethodWebRequest(
        "http://localhost:8080/httpunitapp/login.jsp");
    WebResponse response = wc.getResponse(request);
    
    // ID 入力値
    String id = "abc";
    String wrongPWD = "aaa";

    //(1)
    // 入力フォーム
    WebForm form = response.getForms()[0];
    form.setParameter("loginID", id);
    form.setParameter("loginPWD", wrongPWD);
    // 送信実行
    form.submit();
    
    // 遷移後の画面
    // ------------------------------------------------------------
    response = wc.getCurrentPage();
    //System.out.println(response.getText());
    
    // テスト実施
    
    // 遷移画面 ID
    String sid = response.getHeaderField("X-CZ-SID");
    assertEquals(sid, "SCZ001");
    
    //(2)
    // メッセージコード
    HTMLElement elem = response.getElementWithID("msgCode");
    String msgCode = elem.getText(); 
    assertEquals(msgCode, "3");
    
    // 前回入力した値が再表示されていること
    form = response.getForms()[0];
    String loginID = form.getParameterValue("loginID");
    assertEquals(loginID, id);
}

 基本的な流れは、先のtestErrorID()と同様です。パスワードの入力が追加となっている点と(1)、パスワードの誤りを示すメッセージコードとなっている点(2)、に差異があるだけです。

LoginTest#testLoginComplete()

 ログインが正常に完了したテストケースです。

LoginTest.java(一部抜粋)
/**
 * ログイン正常終了
 *  
 * @throws MalformedURLException
 * @throws IOException
 * @throws SAXException
 */
public void testLoginComplete() throws MalformedURLException,
    IOException, SAXException {
    // 初期画面表示
    // ------------------------------------------------------------
    WebRequest request = new GetMethodWebRequest(
        "http://localhost:8080/httpunitapp/login.jsp");
    WebResponse response = wc.getResponse(request);
    
    // ID 入力値
    String id = "bbc";
    String pwd = "yyy";
    
    // 入力フォーム
    // 1つのみの入力フォームなので、配列位置は「0」
    WebForm form = response.getForms()[0];
    form.setParameter("loginID", id);
    form.setParameter("loginPWD", pwd);
    // 送信実行
    form.submit();
    
    // 遷移後の画面
    // ------------------------------------------------------------
    response = wc.getCurrentPage();
    //System.out.println(response.getText());
    
    // テスト実施
    
    // (1)
    // 遷移画面 ID
    String sid = response.getHeaderField("X-CZ-SID");
    assertEquals(sid, "SCZ002");
    
    // (2)
    // メッセージコード
    HTMLElement elem = response.getElementWithID("msgCode");
    String msgCode = elem.getText(); 
    assertEquals(msgCode, "0");
    
    // (3)
    // ユーザー情報
    // 1つのみのテーブルなので、配列位置は「0」
    String[][] cell = response.getTables()[0].asText();
    // (4)
    // セル位置は「縦×横」となっており、ゼロスタート
    assertEquals(cell[0][1], "bbc");
    assertEquals(cell[1][1], "伊藤");
    assertEquals(cell[2][1], "神奈川県");
    assertEquals(cell[3][1], "22");
}

 今までのエラーのケースと違い、遷移先の画面が変わりますが、画面IDのチェックと、メッセージコードのチェックの仕方自体は変わりません(1)(2)

 大きく変わるのはユーザー情報の表示部分です。ユーザー情報の表示部分はテーブルを使用しています。これまでですと、基本的にはタグ一つ一つを特定して値を取得するイメージでしたが、テーブルの場合、表形式での位置関係をイメージしての操作が可能です。

 まずWebResponse#getTables()というメソッドにより、テーブルタグのオブジェクトを取得します。フォームタグの時と同様に複数のオブジェクトが存在する場合がありますので、配列位置を指定する必要があります。この画面ではテーブルは一つしかないので、フォームの時と同様に0を指定します(3)

 取得したテーブルオブジェクトは縦軸と横軸を持ったStringの配列となっています。ここのでのテスト項目としては、動的なデータを表示する2列目のデータ部分について上から順に評価しています(4)

LoginTest#testErrorTerminal()

 最後に不正な端末からアクセスがあった場合のテストケースです。

LoginTest.java(一部抜粋)
/**
 * 不正な端末からのアクセス
 * 
 * @throws MalformedURLException
 * @throws IOException
 * @throws SAXException
 */
public void testErrorTerminal() throws MalformedURLException,
    IOException, SAXException {
    // (1)
    // 端末 ID を設定
    wc.setHeaderField("X-CZ-TID", "XXX");
    
    // 初期画面表示
    // ------------------------------------------------------------
    WebRequest request = new GetMethodWebRequest(
        "http://localhost:8080/httpunitapp/login.jsp");
    WebResponse response = wc.getResponse(request);
    
    // 入力フォーム
    // 今回は1つのみの入力フォームなので、配列位置は「0」
    WebForm form = response.getForms()[0];
    // 送信実行
    form.submit();
    
    // 遷移後の画面
    // ------------------------------------------------------------
    response = wc.getCurrentPage();
    //System.out.println(response.getText());
    
    // テスト実施
    
    // 遷移画面 ID
    String sid = response.getHeaderField("X-CZ-SID");
    assertEquals(sid, "SCZ001");
    
    // メッセージコード
    HTMLElement elem = response.getElementWithID("msgCode");
    String msgCode = elem.getText(); 
    assertEquals(msgCode, "1");
    
    // (2)
    // 他のケースに影響しないようクリアする
    wc.setHeaderField("X-CZ-TID", null);
}

 WebConversation#setHeaderField()メソッドにて、ヘッダフィールド名を指定して値を設定します(1)

 リクエストヘッダなので、WebRequest#setHeaderField()を使用すると考えるのが普通ですが、今までのコードにあるように、最初のリクエスト送信後、表示した画面を操作した上でWebForm#submit()で送信処理が実行されてしまうため、事前に設定しておく必要があります(単に最初のリクエストを送信して終わるのであれば、WebRequest#setHeaderField()で指定して問題ありません)。

 それ以降は、入力値の部分など省力していますが、今までの流れと同様です。

 テストメソッドの終了時には、メソッド冒頭で設定したヘッダ値をクリアします。これは、他のテストメソッドに影響しないようにするためです(2)

まとめ

 以上、HttpUnitを利用したブラウザユーザーインターフェイスでのテストの自動化について記述しました。

  • xUnitを使用するように、ブラウザUIでも自動テストができます。
  • サーバー側での実装の必要がありません(サーバー実装に依存しません)。
  • 手操作では行えないHTTPヘッダの入力と評価ができます。

 画面のテストは、最終的には実際に操作して、目視で確認する作業が必ず必要です。しかし、最終的に確認するに至るまでの作業については、本件のような手法を利用して省力化することが可能だと思います。Webシステムの画面開発、テストに利用してみてはいかがでしょうか。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

鈴木 等(スズキ ヒトシ)

ルークシステムズ代表。コードの重要性を認識しつつ、効率と品質が両立出来る開発を目標に日夜業務に励んでいます。オープンソースツールを利用するだけでなく、自社開発ツール「SteadyForm」を活用して、テストドリブンな開発を推進していこうとしています。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/618 2008/08/26 14:08

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング