プログラミング言語の選択

テスト設計に関する考慮??

テストのデザインを始める??

この章では初めてテストの自動化をされる方、経験豊かなQAの専門家、その両方に有益になるであろう情報を提供します。テストの自動化の最も一般的なタイプについて述べます。又、自動化されたテストの保守性、拡張性を高めるための一般的なデザインパターンについても述べます。これらのテクニックをまだ使っていない経験豊かな読者も興味深いものを発見するでしょう。

テストのタイプ??

アプリケーションのどの部分をテストするべきでしょうか?それはプロジェクトの特性に依存します:ユーザーの期待、プロジェクトに許された時間、プロジェクトマネージャーによって設定された優先順位等です。プロジェクトの範囲が決定されたら、テスターであるあなたは確実に何をテストするかについての多くの決定をすることになります。

私達は、ウェブアプリケーションの上で行なうテストのタイプを分類するためにここでいくつかの用語を作成しました。これから述べるコンセプトはウェブアプリケーションテストのための典型的なものですが、これらの用語は決して標準のものではありません。

静的なコンテンツのテスト??

最も簡単なタイプのテストはコンテンツのテストで、静的で変わらない UI要素の存在についてのテストです。例えば

  • 各ページは期待されているページタイトルを持っているか?これは、リンクをたどった後に、期待されているページが見つけられたかどうかを確認するために使われます。
  • アプリケーションのホームページは、ページのトップに期待されるイメージを含んでるか?
  • ウェブサイトの各ページには会社へのコンタクトのページ、プライバシー方針のページへのリンク、および商標情報が入ったフッターエリアがあるか?
  • 各ページは、<h1> タグに続く表題テキストから始まっているか?そしてそのヘッダーの中のテキストは正しいものか?

コンテンツのテストは必要かもしれないし、そうでないかもしれません。ページの内容が変わらないものであれば、手動でテストしたほうが効率的でしょう。もし、あなたのアプリケーションが関係しているファイルが別の場所に移動することがあるならば、コンテンツテストは有用であるかもしれません。

機能テスト??

これらは、アプリケーションの中での特定の機能のテストであり、あるタイプのユーザー入力を要求し、あるタイプの結果を戻すものになるでしょう。機能テストは複数のフォームベースの入力ページから構成され、そこには入力フィールドやサブミット、キャンセルの操作ボタンがあり、それらの応答として1つ若しくは複数のページを生じることがあります。ユーザー入力にはテキスト入力フィールド、チェックボックス、ドロップダウンリスト、その他ブラウザでサポートされた入力形式があります。

機能テストは自動化するテストの中で最も複雑なテストであり得ますが、最も重要なテストでもあり得ます。典型的なテストとしてはログインやサイトへの登録、ユーザーアカウントの操作、アカウントの設定、変更などがあり、他に比べて複雑なデータを読み取ることになります。機能テストは一般に、あなたのアプリケーションの機能とデザインを特定するユーザーシナリオを反映しています。

動的要素のテスト??

Webページ要素には、ページの中でその要素を特定するためのユニークな ID が付けられていることがあります。通常、これらは、htmlタグの‘id’属性や‘名前’属性を使って、実現されています。これらの名前は静的なもの、つまり不変の文字列定数であるかもしれません。それらは、また、ページのインスタンスによって変わる動的に生成された値であるかもしれません。例えば、ウェブサーバーは、ユーザーがどんな‘文書’を取り出していたかによって、あるページのインスタンスで表示された文書 doc3861 を別のページのインスタンスでは doc6148 と名付けるかもしれません。文書が存在していることを確認するテストスクリプトがその文書を特定するための ID は固定のものとは限りません。このような変化する ID を持つ動的要素はユーザーのアクションの結果としてのページ上によく現れます。もちろんこれはウェブアプリケーションの機能に依存しますが。

例をあげてみましょう。

<input type="checkbox" value="true" id="addForm:_ID74:_ID75:0:_ID79:0:
    checkBox"/>

これはチェックボックスのHTMLタグを表しています。そのID(addForm:_ID74:_ID75:0:_ID79:0:checkBox)は動的に生成された値です。同じページが次回開かれる時、それはたぶん違う値になるでしょう。

Ajax テスト??

Ajax は、ブラウザがページをリロードすることなしに、アニメーションや、RSS フィード、リアルタイムアップデートデータなど、動的に変化するユーザーインターフェイス要素をサポートするテクノロジーです。Ajax が、Webページの上の要素をアップデートするには無数の方法があります。しかし、簡単に考えれば、Ajax を使用したアプリケーションではデータは取り出された後、ページ全体を再ロードすることなくページ上に表示されます。ページの一部分だけ、若しくは厳密に要素そのものだけが再ロードされます。

結果の検証??

アサートとベリファイ??

いつアサートコマンドを使うべきで、いつベリファイコマンドを使うべきでしょうか?これはあなたに任せられています。チェックが失敗した時に、あなたがどうしたいかによって違います。テストを終わらせたいか、チェックが失敗したことを記録してテストを続けたいかです。

ここにトレードオフがあります。アサートを使うならば、テストはそのポイントで停止し、その後のチェックも実行されません 。これがあなたが望んでいるものであることも多いでしょう。テストが失敗した時に、直ちに、テストが通過しなかったことを知ることができます。TestNG や JUnit などのテストエンジンには一般的に使われる開発環境(第5章)上でテストが失敗したことを知らせる便利なプラグインがあります。有利な点:チェックが通過したかどうかが即時にビジュアルにわかります。不利な点:チェックが失敗した時に、実行されなかった他のチェックの結果については情報を得ることができません。

一方、ベリファイコマンドはテストを終わらせません。もしベリファイコマンドだけを使うならチェックで不備を見つけたかどうかにかかわらずテストは完了まで実行されます。(予期しない例外が起こらないと仮定して) 不利な点:テスト結果を検討するためにより多くの労力を必要とします。すなわち、TestNG や JUnit からのフィードバックを得ることができません。コンソールプリントアウトやログアウトプットの結果を見る必要があります。そして、テストを実行するたびに、時間をとってアウトプットに目を通す必要があります。もしあなたが数百回のテストを実行し、そのたびにログを見るとすると、それには時間がかかります。アサートを使用して即時にフィードバックを得る方がより適切です。アサートの方が即時性の点でベリファイより、より一般的に用いられます。

トレードオフ: assertTextPresent, assertElementPresent, assertText??

あなたはこれらのコマンドとそれらを使用したときのメカニズムに精通しておく必要があります。もしそうでなければ、最初に第3章を参照してください。あなたはテストを作成する時に、次の事を決める必要があります。

  • テキストがページに存在していることをチェックするだけでよいか? (verify/assertTextPresent)
  • HTML要素がページに存在していることをチェックするだけでよいか?すなわち、テキストやイメージ、他のコンテンツなどの内容はチェックせずに、関連するHTMLタグだけをチェックすればよいか? (verify/assertElementPresent)
  • それらの両方、すなわち、要素とテキストコンテンツ両方をチェックしなければならないか? (verify/assertText)

正しい答えというものはありません。それはあなたのテストの要件に依存します。もちろん、あなたがテストしているアプリケーションの要件に依存します。迷った場合は、最も厳密なタイプのチェックポイントである assertText を使ってください。後でいつでもその変更はできますが、少なくともどのような起こりうる失敗も見逃さずにすむでしょう。

Verify/assertText最も具体的なテストタイプです。HTML 要素(タグ)またはテキストのどちらかが、予期しているものではないならば、テストは失敗します。例えば、ウェブデザイナーが頻繁にページを変更し、変更自身が周期的に予測されている場合には、その変更のたびに、テストに失敗してほしくはないでしょう。しかし、パラグラフや表題テキスト、イメージなどの何かがまだページにあることをチェックする必要がある場合には verify/assertElementPresent を使えます。それは、特定のタイプの要素が存在していることを保証してくれます。( XPath を使用してページ内の他のオブジェクトと関連した位置に存在することを確認できるなら) しかし、あなたは、内容が何であるかを気にする必要はありません。特定の要素、例えばイメージが特定の位置にあることだけを気にすればいいだけです。

これらのどのタイプを使うかの判断は時間と少しの経験を積めば得られるようになるでしょう。容易な概念であり、テストの中での変更も簡単です。

位置付け戦略??

位置付け戦略を選択します。??

ページ内のオブジェクトを特定するには複数の方法があります。しかし、それらロケータタイプそれぞれのトレードオフは何でしょうか?オブジェクトを特定する方法を思い出してください。

  • 要素のID
  • 要素の名前属性
  • XPath 文
  • リンクテキスト
  • 文書オブジェクト・モデル(DOM)

要素の ID や名前のロケータを使うのはテストの効率の点で最も効果的で、また、ページソースの中の ID や名前に良い名前が付いている限り、テストのコードをより読み取りやすいものにします。XPath 文はブラウザが XPath プロセッサを動かさなければならないので、処理により長い時間がかかります。XPath は、Internet Explorer バージョン7 で特に遅いことが知られています。リンクテキスト経由で位置付けすることはしばしば便利で、よく機能します。しかしこのテクニックはリンクに特有のものです。また、リンクテキストが、頻繁に変わりそうならば、 要素によって位置付けする方がよりよい選択といえるでしょう。

しかし、XPath ロケータを使わなければならない場合もあります。ページソースが ID や名前属性を持っていなければ、XPath ロケータを使うしかありません。( XPath の方が、できることが多く、よくできるので、DOM ロケータは一般的にもう使われません。DOM ロケータは、単に旧式のテストをサポートするためだけにあります。)

XPath を使用することには ID や名前属性による位置付けにはない利点があります。XPath (そしてDOMも) はページ内の別のオブジェクトに関連して位置付けを行うことができます。例えば、

セクションの中の2番目の節の中で指定されなければならないリンクがあるならば、それに位置づけるために XPath を使うことができます。ID と名前ロケータでは、ページの中のどこかに存在するということに対して位置付けできるだけです。会社ロゴを表示しているイメージがトップページのヘッダーセクションの中にあることをテストしなければならないような場合には XPath がよりよいロケータであるでしょう。

動的要素の位置付け??

テストのタイプのセクションの中で説明したように、動的要素とは ID がページのインスタンスによって変わるようなページの要素です。例えば

<a class="button" id="adminHomeForm" onclick="return oamSubmitForm('adminHomeForm',
    'adminHomeForm:_ID38');" href="#">View Archived Allocation Events</a>

この HTML アンカータグは "adminHomeForm" の ID 属性によってボタンを定義します。ほとんどのHTMLタグと比べるとかなり複雑なアンカータグですが、やはり静的なタグです。HTMLは、このページがブラウザの中でロードされるたびに同じになるでしょう。ID はこのページのすべてのインスタンスで一定であり続けます。すなわち、このページが表示されるたびに UI要素はいつもこの ID を持つでしょう。従って、このボタンをクリックするあなたのテストスクリプトは単に以下のセレニウムコマンドを使うだけですみます。

click       adminHomeForm

またはセレニウム1.0では

selenium.click("adminHomeForm");

しかし、アプリケーションによっては、ウェブページ上の異なるインスタンス上では異なる ID になるように、動的にHTMLを生成する場合もあります。例えば、動的なページ要素のための HTML は次のようになるかもしれません。

<input type="checkbox" value="true" id="addForm:_ID74:_ID75:0:_ID79:0:checkBox"
    name="addForm:_ID74:_ID75:0:_ID79:0:checkBox"/>

これはチェックボックスを定義しています。その ID と名前属性(両方ともaddForm:_ID74:_ID75:0:_ID79:0:checkBox)は動的に生成された値です。このような場合に、標準のロケータを使うと以下のような感じになります。

click       addForm:_ID74:_ID75:0:_ID79:0:checkBox

または、Selenium-RC では

selenium.click("addForm:_ID74:_ID75:0:_ID79:0:checkBox");

動的に生成された ID ではこのアプローチはうまくいかないでしょう。このページが次回ロードされる時、ID は Selenium コマンドの中で使われたものとは違う値になり、、それゆえ、この要素は見つからりません。クリック操作は「要素は見つかりませんでした」エラーを告げ、失敗するでしょう。

これを修正する簡単な解決策は、まさに、ID ロケータを使うよりも XPath ロケータを使うということになります。つまり、チェックボックスでは、あなたは単に次のようにすれば良いのです。

click       //input

または、それが、ページの最初のインプット要素ではないならば(それはたぶんないでしょうが)、より詳細な XPath ステートメントを試してください。

click       //input[3]

又は

click       //div/p[2]/input[3]

しかし、あなたが、要素を位置付けるために ID を使う必要があるならば、違う解決策が必要です。Selenium コマンドの中で使うための ID をウェブサイト上から求めることができます。それはこのようにします。

String[] checkboxids  = selenium.getAllFields(); // ページ上のすべての入力要素の ID を収集
             for(String checkboxid:checkboxids) {
                    if(checkboxid.contains("addForm")) {
                selenium.click(expectedText);
            }
             }

ID がテキスト ‘expectedText’ であるチェックボックスが一つだけしかないならば、このアプローチはうまくいくでしょう。

Ajax 要素の位置付け??

テストタイプのところで述べように、Ajax を用いて実装されたページ要素は、ページ全体をリフレッシュすることなく動的にリフレッシュすることができる要素です。Ajax 要素を特定して、確認する最もよい方法は、Selenium 2.0 WebDriver API を使うことです。これは、Selenium 1 での Ajax 要素のテストで見られる制限に対処するように特にデザインされたものです。

Selenim 2.0において、ページの要素が入手可能になるのを待つためには waitFor() を使用します。パラメータは、WebDriver がロケータを実装する方法を指定するために使われる By オブジェクトです。これについては WebDriver の章で詳述します。

Selenium 1.0( Selenium-RC)ではもう少しコードが必要ですが、それはそんなに難しくありません。アプローチとしては要素をチェックし、入手可能でないならば、あらかじめ決められた時間待って再チェックを行うというものです。そして、要素が見つからないならば前もって決められたタイムアウトによって終わらせるまでこれを繰り返します。

ボタンのクリックによってリンクページ (link=ajaxLink) を表示する (ページをリフレッシュすることなしに)ページを例にとってみましょう。Selenium で for ループを使って以下のように処理できます。

// ループの初期化
for (int second = 0;; second++) {

     // 60 秒過ぎた場合はループを終了させる。
     if (second >= 60) break;

     // 要素 "link=ajaxLink" を検索し、見つかったらループを終了させる。
     try { if (selenium.isElementPresent("link=ajaxLink")) break; } catch (Exception e) {}

     // 1 秒休止
     Thread.sleep(1000);

}

これは確かに唯一の解決策ではありません。Ajax はユーザーフォーラムで共通のトピックであり、他の人が何をしているかを見るために過去の議論を検索されることをお勧めします。

Selenium 呼び出しのラップ??

プログラミングの常として、あなたは、複数のテストにわたってコピーされるであろうコードを処理するユーティリティ機能を使いたいでしょう。これを行う一つの方法は、あなた自身がデザインした機能またはクラスメソッドによって頻繁に使われる Selenium 呼び出しをラップすることです。例えば、多くのテストで、頻繁にページの要素をクリックし、ページがロードされるのを待つことが多くあるでしょう。

selenium.click(elementLocator);
selenium.waitForPageToLoad(waitPeriod);

このコードをコピーするのではなく、両方の機能を実行するラッパーメソッドを書いて使うことができます。

/**
 * クリックし、ページがロードされるのを待ちます。
 *
 * param elementLocator
 * param waitPeriod
 */
public void clickAndWait(String elementLocator, String waitPeriod) {
        selenium.click(elementLocator);
        selenium.waitForPageToLoad(waitPeriod);
}

要素が存在することを確認する‘安全な操作’??

Selenium のラッパーメソッドでよく使われる別の例は要素に対して何かの操作をする前にその存在をチェックしておくことです。これはしばしば&8216;安全な操作&8217;と呼ばれます。例えば、以下のメソッドは期待している要素が存在していること前提としている‘安全な操作’の実装として使われます。

/**
 * Selenum-RC -- 要素がページ上に存在するときのみクリックする。
 *
 * 引数 elementLocator
 */
public void safeClick(String elementLocator) {
        if(selenium.isElementPresent(elementLocator)) {
                selenium.click(elementLocator);
        } else {
                // TestNG API を使ってログする。
                Reporter.log("要素: " +elementLocator+ ", はページ上に見つかりません。 - "
                                +selenium.getLocation());
        }
}

この例は Selenium 1 API を使っていますが、Selenium 2 でもこれはサポートされています。

/**
 * Selenium-WebDriver -- 要素がページ上に存在するときのみクリックする。
 *
 * 引数 elementLocator
 */
public void safeClick(String elementLocator) {
        WebElement webElement = getDriver().findElement(By.XXXX(elementLocator));
        if(webElement != null) {
                selenium.click(elementLocator);
        } else {
                // TestNG API を使ってログする。
                Reporter.log("要素: " +elementLocator+ ", はページ上に見つかりません。 - "
                                + getDriver().getUrl());
        }
}

この2番目の例で ‘XXXX’ は単に、幾つかの位置付けメソッドの一つがここで呼ばれるというだけの意味です。

安全なメソッドを使うことはテスト開発者の裁量にまかされています。それゆえ、見つからなかった要素があってもテストの続行を望む場合は安全なメソッドを使用して見つからなかったことをログしながらテストを続行します。これは、本質的に、アサートでテストを中止するのではなく、レポート機能と共にベリファイを使用することを意味します。しかし、テストを続行する前に特定の要素が必ずなければならないような場合(例えばポータルのホームページの上のログインボタンの存在)にはこの安全なメソッドの手法は使われるべきでありません。

UI マッピング??

UI マップは、テストにおける全てのロケータを一つの場所に集めておいて、UI 要素への ID またはパスが AUT において変わった時に、修正を容易にするためのメカニズムです。テストスクリプトはテストされる要素に位置付けるために UI マップを使用します。UI マップは基本的にテストされるアプリケーションの UI 要素に対応したテストスクリプトオブジェクトのレポジトリーです。

UI マップはなぜ有用か?主な目的はテストスクリプトの管理を容易にすることです。ロケータを編集する必要がある時に、そのオブジェクトについてテストスクリプトコードの中を捜しまわるよりも、中心的な場所があった方が容易に見つけることができます。そして ID を変更するとき、テストスクリプトの中の複数の場所もしくは複数のテストスクリプトで変更を行うのではなく、ひとつの場所で変更を行えます。

要約すると、UI マップには2つの大きな利点があります。

  • UI オブジェクトをスクリプトのあちらこちらで記述するかわりに一つの中心的な場所で記述します。これはスクリプトの保守をより効率的にします。
  • 暗号のような HTML ID や名前の代わりにより解りやすい名前を使うことができ、スクリプトの可読性を高めます。

次の解りにくい ( Java による) 例を見てください。

public void testNew() throws Exception {
             selenium.open("http://www.test.com");
             selenium.type("loginForm:tbUsername", "xxxxxxxx");
             selenium.click("loginForm:btnLogin");
             selenium.click("adminHomeForm:_activitynew");
             selenium.waitForPageToLoad("30000");
             selenium.click("addEditEventForm:_IDcancel");
             selenium.waitForPageToLoad("30000");
             selenium.click("adminHomeForm:_activityold");
             selenium.waitForPageToLoad("30000");
}

このスクリプトは AUT のページのソースに精通していない者にとっては追いかけにくいものです。アプリケーションの常連ユーザーでさえスクリプトが何をするかを理解しがたいでしょう。より良いスクリプトは次のようなものです。:

public void testNew() throws Exception {
             selenium.open("http://www.test.com");
             selenium.type(admin.username, "xxxxxxxx");
             selenium.click(admin.loginbutton);
             selenium.click(admin.events.createnewevent);
             selenium.waitForPageToLoad("30000");
             selenium.click(admin.events.cancel);
             selenium.waitForPageToLoad("30000");
             selenium.click(admin.events.viewoldevents);
             selenium.waitForPageToLoad("30000");
}

UI マップ ID とともにいくつかのコメントと空白を使うことはスクリプトを非常に読みやすくします。

public void testNew() throws Exception {

             // アプリケーションの URL をオープン
             selenium.open("http://www.test.com");

             // admin のユーザ名を設定
             selenium.type(admin.username, "xxxxxxxx");

             // Login ボタンをクリック
             selenium.click(admin.loginbutton);

             // Create New Event ボタンをクリック
             selenium.click(admin.events.createnewevent);
             selenium.waitForPageToLoad("30000");

             // Cancel ボタンをクリック
             selenium.click(admin.events.cancel);
             selenium.waitForPageToLoad("30000");

             // View Old Events ボタンをクリック
             selenium.click(admin.events.viewoldevents);
             selenium.waitForPageToLoad("30000");
}

UI マップの実装には幾つかの方法があります。ロケータのストリング変数を public に持つだけのクラス又は構造体を作成するのが一つの方法です。別の方法としてキー名とその値のペアを持つテキストファイルを持つ方法もあります。Java ではキー名/キー値のペアを持つプロパティファイルを使用するのが最良の方法でしょう。

プロパティファイル prop.properties を UI 要素の ID の読みやすい別名と思ってください。前の例では

admin.username = loginForm:tbUsername
admin.loginbutton = loginForm:btnLogin
admin.events.createnewevent = adminHomeForm:_activitynew
admin.events.cancel = addEditEventForm:_IDcancel
admin.events.viewoldevents = adminHomeForm:_activityold

ロケータは依然、html オブジェクトを参照していますが、テストスクリプトと UI 要素の間に抽象化レイヤーを導入することができました。プロパティファイルから値が読まれて、テストクラスで UI マップを実装するのに使われます。Java プロパティーファイルの詳細についてはこのリンクを参照して下さい。

ページオブジェクトデザインパターン??

ページオブジェクトは、テストの保守性を高め、コードの重複をなくすべく、テストの自動化においてポピュラーになった一つのデザインパターンです。一つのページオブジェクトは、あなたの AUT の一つのページへのインタフェースとなるオブジェクト指向のクラスです。テストが あるページの UI と対話する必要がある時はそのページのページオブジェクトメソッドを使用します。これによる利点は、ページの UI が変更されても、テスト自身を変更する必要がないことです。新しいUIをサポートするすべての変更は一箇所で行われます。

ページオブジェクトデザインパターンは以下の利点をもたらします。

1. テストコードと、ロケータ(または、あなたが UI マップを使っているならばその使用)やレイアウトなどのページに特有なコードの間に、クリーンな分離を設けます。

2. ページによって提供されるサービスや操作をテストのあちこちに散在させるのではなく、一つのレポジトリーで管理することができます。

どちらも UI の変更によるテストの変更を一箇所で行なうことを可能にします。このテストデザインパターンは広く使用されるようになってきたのでたくさんのブログで有用な情報を得ることができます。これについてもっと知りたい方はインターネット上のブログでこの件について検索されることをお勧めします。多くの人がこのデザインパターンについて書いていて、このユーザーガイドで述べる範囲以上に有用な情報を提供しています。しかし、手始めとして簡単な例によってページオブジェクトを説明してみましょう。

まずページオブジェクトを使わない典型的なテストオートメーションの例を見てみます。

/***
 * ログイン機能のテスト
 */
public class Login {

        public void testLogin() {
                selenium.type("inputBox", "testUser");
                selenium.type("password", "my supersecret password");
                selenium.click("sign-in");
                selenium.waitForPageToLoad("PageWaitPeriod");
                Assert.assertTrue(selenium.isElementPresent("compose button"),
                                "Login was unsuccessful");
        }
}

このアプローチには2つの問題点があります。

  1. テストメソッドとAUT ロケータ(この例では ID)が分離されていません;両方が一つのメソッドの中でごちゃまぜになっています。もし、AUT の UI で、その ID 、レイアウトや、ログインがどう入力されてどう処理されるか、が変更されるならば、テスト自身も変更されなければなりません。
  2. id ロケータは、このログインページを使う必要があるすべてのテスト上のあちらこちらに散在することになります。

ページオブジェクトテクニックを使用して、この例を以下のようなサインインページのページオブジェクトを使用した例に書きなおすことができます。

/**
 * サインインページをカプセル化したページオブジェクト
 */
public class SignInPage {

        private Selenium selenium;

        public SignInPage(Selenium selenium) {
                this.selenium = selenium;
                if(!selenium.getTitle().equals("サインインページ")) {
                        throw new IllegalStateException("このページはサインインページではありません。現在のページは: "
                                        +selenium.getLocation());
                }
        }

        /**
         * 有効なユーザでログイン
         *
         * @param userName
         * @param password
         * @return HomePage object
         */
        public HomePage loginValidUser(String userName, String password) {
                selenium.type("usernamefield", userName);
                selenium.type("passwordfield", password);
                selenium.click("サインイン");
                selenium.waitForPageToLoad("waitPeriod");

                return new HomePage(selenium);
        }
}

そして、ホームページのページオブジェクトは次のようになります。

/**
 * ホームページをカプセル化したページオブジェクト
 */
public class HomePage {

        private Selenium selenium;

        public HomePage(Selenium selenium) {
                if (!selenium.getTitle().equals("ログインされたユーザのホームページ")) {
                        throw new IllegalStateException("これはログインされたユーザのホームページではありません。現在のページは" +
                                        "is: " +selenium.getLocation());
                }
        }

        public HomePage manageProfile() {
                // プロファイル機能を管理するページのカプセル化
                return new HomePage(selenium);
        }

        /* ログインされたユーザのホームページが提供するサービスのためにこの他に多くのメソッドが提供されます。これらのメソッドは多くのページオブジェクトを返すことになります。例としてメール作成のボタンはメール作成オブジェクトを返すでしょう。 */
}

ログインテストはこれらの二つのページオブジェクトを使って次のようになります。

/***
 * ログイン機能のテスト
 */
public class TestLogin {

        public void testLogin() {
                SignInPage signInPage = new SignInPage(selenium);
                HomePage homePage = signInPage.loginValidUser("userName", "password");
                Assert.assertTrue(selenium.isElementPresent("作成ボタン"),
                                "ログイン失敗");
        }
}

ページオブジェクトのデザインの方法には大きな柔軟性がありますが、テストコードに期待された保守性を持たせるためにはいくつか基本的な規則があります。ページオブジェクト自身では決してベリファイやアサートを行なってはなりません。これらはページオブジェクトの中ではなく、テストの一部としてテストコードの中で行わなければなりません。ページオブジェクトは、ページの表現と、ページがメソッド経由で提供するサービスを含むでしょうが、テストされるものと関連したどのようなコードも含めるべきではありません。

ページオブジェクトの中で行なってもいい、そして行うべきベリファイが一つだけあり、それはページとページの中になくてはならない要素が正しくロードされていることのベリファイです。このベリファイはページオブジェクトのインスタンス化のときに行われるべきです。上記の例ではサインインページとホームページのコンストラクタが期待されたページが存在し、テストからも要求を受け付けられることをチェックしています。

ページオブジェクトは、必ずしもページ全体を表す必要はありません。ページオブジェクトデザインパターンは、一つのページの中の複数のコンポーネントを表すために使うこともできます。もし AUT のページが複数のコンポーネントを持っているなら、一つのコンポーネントごとに一つのページオブジェクトを割り当てることで保守性を改善することができるでしょう。

テストに用いられるデザインパターンは他にもあります。ページオブジェクトをインスタンス化するために、ページファクトリーが使われることがあります。これらのすべてについて述べることはこのユーザーガイドの範囲を越えています。ここでは単に読者にいくつかの有用な概念を紹介するだけに留めます。先に述べたように、多くの人がこのトピックについてブログを書いており、読者にはこれらのトピックについてブログを検索するようにお勧めします。

データ駆動テスト??

データ駆動テストは、データを変えながら同じテスト(又は複数のテスト)を複数回実行することを意味します。これらのデータセットは .csv ファイルやテキストファイルなどの外部ファイルであったり、データベースからロードしたものであったりします。データ駆動テストは、様々に変化する入力に対してアプリケーションの有効性を確かめるために一般的に使われるテスト自動化のテクニックです。テストが変化するデータ用にデザインされていれば、テストコードを変更することなく、入力データを拡大し、本質的に追加のテストを作成することができます。

Python を用いて:

# 文字列値のコレクション
source = open("input_file.txt", "r")
values = source.readlines()
source.close()
# 文字列値の配列中のそれぞれの文字列について繰り返す。
for search in values:
    sel.open("/")
    sel.type("q", search)
    sel.click("btnG")
    sel.waitForPageToLoad("30000")
    self.failUnless(sel.is_text_present("Results * for " + search))

この Python スクリプトはテキストファイルを開きます。ファイルは各行に異なる検索用文字列を含んでいます。コードはこれをストリングの配列に保存し、配列上のそれぞれの文字列について検索とアサートを繰返します。

これは非常に基本的な例ですが、そのアイデアは、様々なデータによってテストを実行することがプログラムやスクリプト言語によって容易にできることを示すことです。スプレッドシートからデータを読み込んだり、TestNG のデータプロバイダー機能を使ったりするより多くの例については、Selenium RC wiki を参照してください。これは Selenium を使わない人も含むテスト自動化のプロの間では良く知られたトピックなので、インターネット上で “data-driven testing”(データ駆動テスト)で検索するとたくさんのブログを見つけることができるでしょう。

データベース認証??

別の一般的なタイプのテストとして UI データと AUT のデータベース に実際に格納されたデータをを比較することがあります。プログラミング言語の中からデータベースへの照会ができるので、あなたがデータベースサポート機能を使えるとすると、あなたはそれらを使ってデータを取り出し、それを使って AUT によって表示されたデータをが正しいかどうか検証することができます。

データベースに登録された電子メールアドレスを取り出して UI と比較する例を考えてみましょう。DB との接続を確立し、データを取り出す例は次のようになるでしょう。

Java を用いて:

// Microsoft SQL サーバー用 JDBC ドライバーをロードします。
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");

// 接続 URL の準備
String url = "jdbc:sqlserver://192.168.1.180:1433;DatabaseName=TEST_DB";

// DB との接続の獲得
public static Connection con =
DriverManager.getConnection(url, "username", "password");

// DDL 及び DML の SQL 文で使われる
// // ステートメントオブジェクトを作成
public static Statement stmt = con.createStatement();

// SQL 文を Statement.executeQuery 経由でデータベースに送信
// これは要求された情報をデータの列として ResultSet オブジェクト
// で返す。

ResultSet result =  stmt.executeQuery
("select top 1 email_address from user_register_table");

// "result" オブジェクトから "email_address" の値を取り出す。
String emailaddress = result.getString("email_address");

// emailAddress の値を使ってアプリケーションにログイン
selenium.type("userID", emailaddress);
selenium.type("password", secretPassword);
selenium.click("loginButton");
selenium.waitForPageToLoad(timeOut);
Assert.assertTrue(selenium.isTextPresent("こんにちは; +emailaddress), "ログインできません。ユーザー名:" +emailaddress)

これはデータベースからのデータ検索の簡単な Java による例です。