Selenium と HTML5 Robot でエンタプライズアプリをテストする
こんにちは、ゼノフィnakamuraです。
ブラウザベースの自動テストの需要は無視できません。企業は IT スタッフを削減していますが、それらの企業では、最新のソフトウェアアップグレードによって、業務上最も重要なアプリケーションが Internet Explorer 7 や Firefox Version 19 で動作する事を確認する必要があります。人間のスタッフが手動でテストし、判断するのは、時間がかかるし、エラーも発生するし、お金もかかるし、本来は必要ありません。
多くの会社では、Selenium を使っています。Selenium は簡単に使用できますし、多く採用されているため、自動テストの企業標準となりましたが、静的な DOM とフォームベースの Web アプリケーションというビジョンは時代遅れです。Sencha Touch や Ext JS などのモダンな Web アプリケーションフレームワークは、ユーザーがアプリケーションを操作すると DOM を変更しますが、これは Selenium とっては頭の痛い問題です。
HTML5 Robot は、HTML5 技術に対して Selenium を利用する際の課題を解決するために作られました。HTML5 Robot は、Ext JS 4, Ext JS 5, Sencha Touch 2 などの HTML5 フレームワークに対して、特定のコンポーネントの参照やインタラクションのサポートを追加し、Selenium の能力を拡張しています。また、企業の Web アプリケーションにおいてコンポーネントの参照やインタラクションを期待通りに動作させる方法のベストプラクティスに従っています。HTML5 Robot は Java フレームワークとして、テストスイートの作成と管理できますし、既存のプロジェクトに含む Java ライブラリとして利用することもできます。
HTML5 とメモリ上の HTML DOM を理解する
長い間、HTML のページは静的なリソースであり、Web サーバーから送られ、変更せずに Web ブラウザで表示されるものでした。「ページのソースを表示」で見えるものが全てです。<select /> コンポーネントは <select /> コンポーネントです。しかし 2008年になると、AJAX と JavaScript のフレームワークが人気になり、静的な Web ページは段々少なくなってきました。ユーザーの操作でDOMを変更するのは普通になり、ページのソースを見ても、それが全てではなくなりました。この理由は、<select /> コンポーネントに <option /> コンポーネントを追加できるからです。この <select /> の変更は、ページソースでは見られません。見るためには、ブラウザのメモリーで存在している HTML DOM を監視する必要があります。Chromeなどのブラウザでは、ページに右クリックし、「要素の検証」を選択すれば見ることができます。
「ページのソースを表示」で表示されるHTML |
「要素の検証」で表示される HTML |
<select name="someName"> |
<select name="someName"> |
Sencha Touch や Ext JS などのフレームワークは、このサンプルのように単純ではありません。Ext JS 5 の場合、<select /> コンポーネント (ドロップダウンとかコンボボックスとも呼ばれます) は <select /> コンポーネントではありません。コンボボックスに入力できる場合は、ベースとなる HTML コンポーネントは input です。しかし、文字を入力できない場合には、ベースとなる HTML コンポーネントはリンク (a タグ) です。例えば、文字を入力できる Ext JS 5 のコンボボックス について検討しましょう。
Ext JS 5
{ fieldLabel: 'Select a single state', displayField: 'state', anchor: '-15', labelWidth: 130, store: { type: 'states' }, minChars: 0, queryMode: 'local', typeAhead: true } |
生成された HTML
<div id="combobox-3234..."> <label id="combobox-3234..."> Select a single state </label> <div id="combobox-3234..."> <input id="combobox-3234..." /> <div id="combobox-3234..." class="x-form-arrow-trigger" /> </div> </div> |
生成された HTML 内のそれぞれの HTML タグの ID 属性に番号が含まれています。この値は動的に生成された事を指しています。Ext JS 5 の場合、ほんの一部の例外を除き、ID のほとんど全ては、動的に生成されます。そのため、この属性に頼るのは良くありません。
Ext JS と Sencha Touch が生成したコンポーネントを効果的かつ正確に探す方法がいくつかあります。この方法はコンポーネントそのものによります。効果的な方法を優先順位で並べると次のようになります。
コンポーネント中の文字によって : これは、リンクやボタンなどのようにコンポーネント内に文字が存在している時にだけ効果があります
フォームラベルによって : コンポーネントが関連するラベルつきフォームに入っている場合、効果的です
存在によって : 全ての場合に効果的ですが、「ページの3番目のコンボボックスを探す」のような手間がかかります
例えば、Ext JS 5 のコンボボックスがあるとします、それは関連しているフォームラベルで取得できます。このロケーションの表現が失敗するのは、ラベルのテキストが変更された時だけです。我々のオプションには、ページが次のロードする時に、動的な属性が関係しているので、予想できます。
例えば、次を行う XPath ステートメントをご覧ください。
コンボボックスの役割をしている input を探す
…「Select a single state」というテキストが含まれているラベルを含む div はどこか
…そして、コンボボックスのボタンとなる div を検索、”x-form-arrow-trigger” クラスで特定する
選択されるエレメントは、赤いボックスの下矢印です
XPath (読み易くするために改行とスペースを追加しています)
//input[contains(@role, 'combobox')] /ancestor::div /descendant::label[contains(.,'Select a single state')] /ancestor::div[1] /descendant::div[contains(@class, 'x-form-arrow-trigger')] |
これで、なにを操作するか理解できるようにもなります。コンボボックスを操作するのは、Ext JS 5 のグリッドとは異なります。この操作メソッドは、コンポーネント内のインタラクティブな部分とその検索方法に集中します。このため、HTML5 Robot は、コンポーネントの検索とその操作ができるサブコンポーネントの定義済みメソッドがあります (Java – Locating Components を参考にして下さい) 。
例えば、Ext JS 5 のサポートクラスが含まれている Java ベースの HTML5 Robot テストでは、次を利用して、Ext JS 5 コンボボックスのボタンの XPath を取得できます:
String xpath = findComboBoxButtonByFormLabel("Select a single state"); |
これは、一般的なHTML, Sencha Touch, Ext JS の複数のバージョンに対して、考えられる操作のポイントを全てカバーしています。テストを作成する際には、本当に大きい時間の節約となり、テストはメンテナンスしやすくなり、壊れにくくなるため、長い目で見ると時間をより節約できます。
エンタプライズのアプリケーションをテストする
モダンな Web アプリケーションで DOM がどのようになっているかしっかり分かったところで、サンプルを見てみましょう。HTML5 Robot は通常 Kitchen Sink のデモに対してテストしますが、Ext JS 5 には、企業向けのアプリケーションをシミュレーションする Executive Dashboard という新しいサンプルが含まれています。ローカルで HTML5 Robot を設置して、動作させる方法を このビデオ でご覧ください。
アプリケーションのテストに関して、最初の手順は、操作したいコンポーネントや検証したいデータが含まれているコンポーネントを特定する事です。
“KPI Overview” にはリンク、ボタン、テキスト、チャートが含まれています。チャートは Canvas をベースにしているため、操作するなら、リンク、ボタン、テキストで利用されているメソッドとは異なります。操作のポイントは上の図でハイライトされています。
最初のステップは、インタラクティブなコンポーネントを取り出すクラスを生成することですので、BaseModel を拡張する Model クラスを生成するところから始めます。
import static com.appfoundation.automation.util.ExtJs5XPathUtils.*; import com.appfoundation.automation.framework.BaseModel; public class ExtJs5ExecutiveDashboardModel extends BaseModel { ... |
ExtJs5ExecutiveDashboardModel.java
次は、後で必要となる、Model で XPath ルックアップをする定数をいくつか定義します。
... // Left menu links public static final String KPI_LINK_XPATH = findLinkByText("KPI Overview"); public static final String PERFORMANCE_LINK_XPATH = findLinkByText("Performance"); public static final String PROFIT_N_LOSS_XPATH = findLinkByText("Profit & Loss"); public static final String COMPANY_NEWS_XPATH = findLinkByText("Company News"); // KPI Overview public static final String KPI_CLICKS_XPATH = findButtonByText("CLICKS"); public static final String KPI_WON_XPATH = findButtonByText("WON"); public static final String KPI_SALES_XPATH = findButtonByText("SALES"); public static final String KPI_GOALS = findButtonByText("GOALS MET"); ... |
ExtJs5ExecutiveDashboardModel.java
XPath のルックアップを定義したら、次のステップは、このコンポーネントに対して Selenium WebElement のルックアップをする事です。
... public ExtJs5ExecutiveDashboardModel(BaseSeleniumTest test) { super(test); } public WebElement findKpiLink() { return this.find(KPI_LINK_XPATH); } public WebElement findPeformanceLink() { return this.find(PERFORMANCE_LINK_XPATH); } ... |
ExtJs5ExecutiveDashboardModel.java
次のステップは Base Test Class を作成する事です。この目的は、全てのブラウザで動作するであろうという、テスト操作の基本を定義する事です。後で、別のクラスでブラウザ専用のテスト実装を定義します。Eclipse などの IDE からクラスのテストメソッドを動かす時には、テストはデフォルトでは Chrome で動作します。Base Test Class は BaseSeleniumTest を拡張する必要があり、これは通常は複雑な操作をよりシンプルにさせる便利なユーティリティへのアクセスを提供します。例えば一つの WebElement を他の上にドラッグアンドドロップする操作を可能にします。その他、BaseModel をインスタンス化しますし、実際のテストの実行も管理します。
package com.appfoundation.automation.example; import org.junit.Test; import com.appfoundation.automation.example.model.ExtJs5ExecutiveDashboardModel; import com.appfoundation.automation.framework.BaseSeleniumTest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; public class ExtJs5ExecutiveDashboardTestBase extends BaseSeleniumTest { private String URL = "http://dev.sencha.com/extjs/5.0.0/examples/executive-dashboard/index.html"; private ExtJs5ExecutiveDashboardModel model; public ExtJs5ExecutiveDashboardTestBase() { this.model = new ExtJs5ExecutiveDashboardModel(this); } @Test public void testKpiOverview() throws Exception { // Code goes here } |
ExtJs5ExecutiveDashboardTestBase.java
注:このテストは JUnit ベースですが、私の好みで Hamcrest でアサーションを扱っています。Hamcrest を使った “Assert that X is Y” などのステートメントの方が、JUnit の “assert that Y is expected from X” より読みやすいと思います。”assertThat” のステートメントの代わりに、JUnit 標準の “assertEquals” も使用できます。
どのテストでも、まずはテストする URL を開くところからスタートします。また、どのテストでも、アプリケーションが操作できる状態になっている事を知るために、決定的な部分を待つ必要があります。これを行うための、良い方法は、ページのある部分がクリックできるまで待つ事です。この場合には、 “KPI Overview” のリンクが使用できるまで待っています。
URLをフルスクリーンで開いて、”KPI Overview” のリンクがクリックできるようになるまで待ちます。
... // テストするページをロードする this.openUrlFullScreen(URL + "#!kpi/clicks"); // wait for the first menu item (a link) to be clickable // 最初のメニューアイテム (a link) がクリックできるようになるのを末 this.waitToBeClickable(ExtJs5ExecutiveDashboardModel.KPI_LINK_XPATH); this.outputScreenShot(); ... |
KPIテストの概要 – ExtJs5ExecutiveDashboardTestBase.java
この時点で、予期される表示データの検証を始めるのがおすすめです。KPI Overview の場合には、ページのトップにリストされた Campaigns, Opportunities, Closed, Total Sales, Goals の予想値をアサートできます。
予期されるデータの検証
... // スクリーン上の一般的なデータの検証 assertThat(model.findBody().getText(), containsString("10\nCAMPAIGNS")); assertThat(model.findBody().getText(), containsString("20,560\nOPPORTUNITIES")); assertThat(model.findBody().getText(), containsString("10,000\nCLOSED WON")); assertThat(model.findBody().getText(), containsString("$90,200\nTOTAL SALES")); assertThat(model.findBody().getText(), containsString("71%\nGOALS MET")); ... |
KPI テストの概要 – ExtJs5ExecutiveDashboardTestBase.java
次は使用可能なオプションをクリックできますので、まずは “WON” ボタンから始めましょう。
“WON” ボタンをクリックし、”CLICKS” ボタンがクリック可能になるまで待つ
... // WON ボタンを選択肢、選択が完了するまで待つ model.findKpiWonButton().click(); this.waitToBeClickable(ExtJs5ExecutiveDashboardModel.KPI_CLICKS_XPATH); this.outputScreenShot(); ...
KPI Overview のテスト – ExtJs5ExecutiveDashboardTestBase.java
テストの実行
Eclipse などの IDE では、コード内のテストメソッドの宣言を右クリックして、 “Run As -> JUnit Test” を選択して下さい。この結果、テストメソッドは、デフォルトブラウザ (Chrome) に対して実行できます。これは Chrome です。もしログの設定が Debug になっていたら、コンポーネントの位置に利用されていた XPath 指定もコンソール出力します。
JUnit – Test KPI Overview –
JUnit – KPI Overview のテスト –
ExtJs5ExecutiveDashboardTestBase.java
コンソール出力
... 12:45:22,273 DEBUG [ExtJs5XPathUtils] findButtonByText: //a[contains(@class,'x-btn') and contains(.,'CLICKS')] 12:45:22,273 DEBUG [ExtJs5XPathUtils] findButtonByText: //a[contains(@class,'x-btn') and contains(.,'WON')] 12:45:22,289 DEBUG [ExtJs5XPathUtils] findButtonByText: //a[contains(@class,'x-btn') and contains(.,'SALES')] ... |
コンソール出力 – KPI Overview のテスト – ExtJs5ExecutiveDashboardTestBase.java
他のブラウザでのテスト
HTML5 Robot はベースクラスを拡張するブラウザ専用のテストクラスを利用します。このサンプルの場合は ExtJs5ExecutiveDashboardTestBase となります。この理由は、ブラウザ専用のテスト実装を可能とし、これは、ファイルアップロードなどのブラウザ特有の操作を扱う際に必要となります。ブラウザ特有なテストを行うためには、テストにアノテーションを指定する必要があります。
例えば、Firefox でテストを実行したい場合は、
Firefox
package com.appfoundation.automation.example.test; import com.appfoundation.automation.example.ExtJs5ExecutiveDashboardTestBase; import com.appfoundation.automation.framework.Browser; import com.appfoundation.automation.framework.BrowserType; @BrowserType(value = Browser.FIREFOX) public class ExtJs5ExecutiveDashboardFirefoxTest extends ExtJs5ExecutiveDashboardTestBase { } |
Eclipse IDE でどのテストクラスを右クリックし、”Run As -> JUnit Test” を選択すると、クラスレベルのアノテーションに指定された通り、テストを適切なブラウザで動作させます。重要なポイントは、利用するブラウザは、利用するコンピュータにインストールされている事です。テストを Internet Explorer 8 に対して動作させたければ、実行するマシンに Internet Explorer 8 がインストールされている必要があります。
まとめ
未来は HTML5 で、Sencha は、Ext JS 5 と Sencha Touch 2 で未来への道をリードしています。 Selenium は素晴らしいのですが、HTML5 アプリケーションが可能にする素晴らしいものに遅れずについて行くのが難しくなって来ました。 HTML DOM のコンポーネントの場所を特定する専門家であっても、Ext JS 5 などに特化した技術で、そのコンポーネントの可能な操作方法を全て定義する必要があります。 HTML5 Robot があると、Ext JS 5 を操作する機能がついてくるだけではなく、企業が Selenium を通して、メンテナンスしやすく信頼性の高いブラウザベースのテストを素早く生成できるベストプラクティスが提供されます。 Chrome, Firefox, Internet Explorer, Safari などのメジャーなブラウザのドライバーのサポートも付いています。 このサポートは、スクリーンショット、ドラッグアンドドロップ、HTML5 の非同期の世界で待機条件式を管理する事などが含まれています。 HTML5 Robot をあなたのプロジェクトに統合する事について詳しくは、 我々のビデオ を参考にして下さい。