Seleniumを使用して、高レベルのWebサイトテスト(モジュールレベルでの広範なpython doctestsに加えて)を処理するのに非常に成功しています。しかし、今では多くのページにextjsを使用していますまた、グリッドなどの複雑なコンポーネントのSeleniumテストを組み込むことが困難であることが判明しています。
ExtjsベースのWebページの自動テストの作成に成功した人はいますか?多くのグーグル検索では、同様の問題を抱えているが、答えはほとんどない人々を見つけます。ありがとう!
SeleniumでExtJSをテストする際の最大のハードルは、ExtJSは標準のHTML要素をレンダリングせず、Selenium IDEは単純に装飾として機能する要素をターゲットにしたコマンドを単純に(そして正当に)生成することです-余分な要素デスクトップ全体のルックアンドフィールを備えたExtJS。以下は、ExtJSアプリに対する自動化されたSeleniumテストを作成する際に集めたいくつかのヒントとコツです。
FirefoxでSelenium IDEを使用してユーザーアクションを記録してSeleniumテストケースを生成する場合、Seleniumは記録されたアクションをHTML要素のIDに基づきます。ただし、ほとんどのクリック可能な要素では、ExtJSは「ext-gen-345」などの生成されたIDを使用します。テストのためのユーザーアクションを記録した後、生成されたIDに依存するすべてのアクションを実行し、それらを置き換える手動の努力が必要です。実行できる置換には2つのタイプがあります。
CSSロケーターは「css =」で始まり、XPathロケーターは「//」で始まります(「xpath =」プレフィックスはオプションです)。 CSSロケーターは冗長性が低く、読みやすく、XPathロケーターよりも優先されるべきです。ただし、CSSロケーターでは単純にカットできないため、XPathロケーターを使用する必要がある場合があります。
一部の要素は、ExtJSによって実行される複雑なレンダリングのために、単純なマウス/キーボード操作以上のものを必要とします。たとえば、Ext.form.CombBoxは実際には<select>
要素ではなく、ドキュメントツリーの下部にあるドロップダウンリストが分離されたテキスト入力です。 ComboBoxの選択を適切にシミュレートするために、最初にドロップダウン矢印のクリックをシミュレートしてから、表示されるリストをクリックすることができます。ただし、CSSまたはXPathロケーターを使用してこれらの要素を見つけるのは面倒です。別の方法は、ComoBoxコンポーネント自体を見つけて、そのメソッドを呼び出して選択をシミュレートすることです。
var combo = Ext.getCmp('genderComboBox'); // returns the ComboBox components
combo.setValue('female'); // set the value
combo.fireEvent('select'); // because setValue() doesn't trigger the event
Seleniumでは、runScript
コマンドを使用して、上記の操作をより簡潔な形式で実行できます。
with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
Seleniumには、ユーザーのアクションによってページの移行またはリロードが発生したときにページのロードを待機するすべてのコマンドに「* AndWait」フレーバーがあります。ただし、AJAXフェッチには実際のページの読み込みは含まれないため、これらのコマンドを同期に使用することはできません。解決策は、AJAX進行状況インジケーターの有無やグリッド、追加コンポーネント、リンクなどの行の外観などの視覚的な手がかりを利用することです。
Command: waitForElementNotPresent
Target: css=div:contains('Loading...')
ユーザーアクションによってビューが変更された後、ExtJSがコンポーネントをレンダリングする速度に応じて、特定の時間後にのみ要素が表示される場合があります。 pause
コマンドで任意の遅延を使用する代わりに、目的の要素が把握されるまで待つのが理想的な方法です。たとえば、表示されるのを待ってからアイテムをクリックするには:
Command: waitForElementPresent
Target: css=span:contains('Do the funky thing')
Command: click
Target: css=span:contains('Do the funky thing')
任意の一時停止に依存することは、異なるブラウザまたは異なるマシンでテストを実行した結果生じるタイミングの違いによりテストケースが不安定になるため、良いアイデアではありません。
一部の要素は、click
コマンドではトリガーできません。これは、イベントリスナが実際にコンテナ上にあり、その子要素でマウスイベントを監視し、最終的に親にバブルするためです。タブコントロールはその一例です。タブをクリックするには、タブラベルでmouseDown
イベントをシミュレートする必要があります。
Command: mouseDownAt
Target: css=.x-tab-strip-text:contains('Options')
Value: 0,0
検証用の正規表現またはvtypeが関連付けられているフォームフィールド(Ext.form。*コンポーネント)は、ユーザーがテキストを入力した後、特定の遅延(デフォルトで250msに設定されているvalidationDelay
プロパティを参照)をトリガーしますまたは、フィールドがフォーカスを失ったとき、つまりブラーが発生したとき(validateOnDelay
プロパティを参照)。 type Seleniumコマンドを発行してフィールド内にテキストを入力した後にフィールド検証をトリガーするには、次のいずれかを実行する必要があります。
遅延検証のトリガー
フィールドがキーアップイベントを受け取ると、ExtJSは検証遅延タイマーを起動します。このタイマーをトリガーするには、ダミーのキーアップイベントを発行するだけで(ExtJSが無視するため、どのキーを使用してもかまいません)、validationDelayよりも長い短い一時停止が続きます。
Command: keyUp
Target: someTextArea
Value: x
Command: pause
Target: 500
トリガーの即時検証
フィールドにblurイベントを挿入して、すぐに検証をトリガーできます。
Command: runScript
Target: someComponent.nameTextField.fireEvent("blur")
検証後、エラーフィールドの有無を確認できます。
Command: verifyElementNotPresent
Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))]
Command: verifyElementPresent
Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))]
エラーフィールドが表示され、非表示にする必要がある場合、ExtJSはエラーフィールドをDOMツリーから完全に削除するのではなく単純に非表示にするため、「display:none」チェックが必要であることに注意してください。
オプション1
コマンド:クリックターゲット:css = button:contains( 'Save')
キャプションでボタンを選択します
オプション2
コマンド:クリックターゲット:css =#save-optionsボタン
IDでボタンを選択します
Command: runScript
Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
最初に値を設定してから、オブザーバーがいる場合に明示的にselectイベントを起動します。
これ blog は私を大いに助けてくれました。彼はこのトピックについて多くのことを書いており、まだ活発なようです。男はまた、良いデザインを高く評価しているようです。
彼は基本的に、javascriptを送信してクエリを実行し、Ext.ComponentQuery.queryメソッドを使用して、extアプリで内部的に行うのと同じ方法でデータを取得することについて話しています。そうすれば、xtypesとitemIdsを使用でき、狂った自動生成されたものを解析しようと心配する必要はありません。
この記事 は、特に非常に役立ちました。
ここでもう少し詳細なものをすぐに投稿するかもしれません-まだこれを適切に行う方法を頭に入れようとしています
ExtJs WebアプリケーションをSeleniumでテストしています。最大の問題の1つは、グリッド内のアイテムを選択して何かをすることでした。
このために、ヘルパーメソッドを作成しました(SeleniumExtJsUtilsクラスは、ExtJとの対話を容易にするための便利なメソッドのコレクションです)。
/**
* Javascript needed to execute in order to select row in the grid
*
* @param gridId Grid id
* @param rowIndex Index of the row to select
* @return Javascript to select row
*/
public static String selectGridRow(String gridId, int rowIndex) {
return "Ext.getCmp('" + gridId + "').getSelectionModel().selectRow(" + rowIndex + ", true)";
}
そして、行を選択する必要があるとき、私はただ電話するでしょう:
Selenium.runScript( SeleniumExtJsUtils.selectGridRow("<myGridId>", 5) );
これが機能するためには、グリッドにIDを設定する必要があり、ExtJに独自のIDを生成させないようにします。
要素が表示されていることを検出するには、次の句を使用します:not(contains(@style, "display: none")
これを使用することをお勧めします:
visible_clause = "not(ancestor::*[contains(@style,'display: none')" +
" or contains(@style, 'visibility: hidden') " +
" or contains(@class,'x-hide-display')])"
hidden_clause = "parent::*[contains(@style,'display: none')" +
" or contains(@style, 'visibility: hidden')" +
" or contains(@class,'x-hide-display')]"
Extjsテストで発生している問題の種類についてより多くの洞察を提供できますか?
私が便利だと思うSelenium拡張機能の1つは、 waitForCondition です。問題がAjaxイベントの問題であると思われる場合は、waitForConditionを使用してイベントが発生するのを待つことができます。
Ext JS Webページは、Ext JSグリッドのように複雑なHTMLを生成するため、テストが難しい場合があります。
HTML5 Robot これは、動的ではない属性と条件に基づいてコンポーネントを確実に検索および操作するための一連のベストプラクティスを使用してこれに対処します。次に、対話する必要があるすべてのHTML、Ext JS、およびSencha Touchコンポーネントでこれを行うためのショートカットを提供します。次の2つのフレーバーがあります。
たとえば、テキスト「Foo」を含むExt JSグリッド行を検索する場合、Javaで次の操作を実行できます。
findExtJsGridRow("Foo");
...そして、グウェンで次のことができます:
extjsgridrow by text "Foo"
Ext JS固有のコンポーネントを操作する方法については、 Java とGwenの両方に関するドキュメントが多数あります。ドキュメントには、これらのすべてのExt JSコンポーネントの結果のHTMLの詳細も記載されています。
Seleniumを使用し、extjsで問題が発生したテストフレームワークを開発しています(クライアント側のレンダリングのため)。 DOMの準備ができたら、要素を探すと便利です。
public static boolean waitUntilDOMIsReady(WebDriver driver) {
def maxSeconds = DEFAULT_WAIT_SECONDS * 10
for (count in 1..maxSeconds) {
Thread.sleep(100)
def ready = isDOMReady(driver);
if (ready) {
break;
}
}
}
public static boolean isDOMReady(WebDriver driver){
return driver.executeScript("return document.readyState");
}
ページ上のグリッドのIDを介してグリッドを取得するための便利なヒント:このAPIからより便利な機能を拡張できると思います。
sub get_grid_row {
my ($browser, $grid, $row) = @_;
my $script = "var doc = this.browserbot.getCurrentWindow().document;\n" .
"var grid = doc.getElementById('$grid');\n" .
"var table = grid.getElementsByTagName('table');\n" .
"var result = '';\n" .
"var row = 0;\n" .
"for (var i = 0; i < table.length; i++) {\n" .
" if (table[i].className == 'x-grid3-row-table') {\n".
" row++;\n" .
" if (row == $row) {\n" .
" var cols_len = table[i].rows[0].cells.length;\n" .
" for (var j = 0; j < cols_len; j++) {\n" .
" var cell = table[i].rows[0].cells[j];\n" .
" if (result.length == 0) {\n" .
" result = getText(cell);\n" .
" } else { \n" .
" result += '|' + getText(cell);\n" .
" }\n" .
" }\n" .
" }\n" .
" }\n" .
"}\n" .
"result;\n";
my $result = $browser->get_eval($script);
my @res = split('\|', $result);
return @res;
}
Senchaドキュメント から:
ItemIdは、オブジェクト参照が利用できない場合にコンポーネントへの参照を取得する代替方法として使用できます。 Ext.getCmpでidを使用する代わりに、itemIdを取得するExt.container.Container.getComponentでitemIdを使用します。 itemIdはコンテナの内部MixedCollectionへのインデックスであるため、itemIdはコンテナにローカルにスコープされ、一意のIDを必要とするExt.ComponentManagerとの潜在的な競合を回避します。
Ext.AbstractComponent
のonBoxReady
メソッドをオーバーライドして、カスタムデータ属性(名前は各コンポーネントのカスタムtestIdAttr
プロパティに由来する)をコンポーネントのitemId
値に設定します(存在する場合) 。 Testing.overrides.AbstractComponent
クラスをapplication.js
ファイルのrequires
配列に追加します。
/**
* Overrides the Ext.AbstracComponent's onBoxReady
* method to add custom data attributes to the
* component's dom structure.
*
* @author Brian Wendt
*/
Ext.define('Testing.overrides.AbstractComponent', {
override: 'Ext.AbstractComponent',
onBoxReady: function () {
var me = this,
el = me.getEl();
if (el && el.dom && me.itemId) {
el.dom.setAttribute(me.testIdAttr || 'data-Selenium-id', me.itemId);
}
me.callOverridden(arguments);
}
});
このメソッドにより、開発者はコード内で記述識別子を再利用し、ページがレンダリングされるたびにそれらの識別子を使用できるようにすることができます。記述的ではない動的に生成されたIDを検索する必要はもうありません。
WebDriverを使用してExtJSアプリケーションをテストしていたとき、次のアプローチを使用しました。ラベルのテキストでフィールドを探し、ラベルから_@for
_属性を取得しました。たとえば、ラベルがあります
_<label id="dynamic_id_label" class="TextboxLabel" for="textField_which_I_am_lloking_for">
Name Of Needed Label
<label/>
_
そして、WebDriverに何らかの入力を指示する必要があります://input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)]
。
したがって、_@for
_属性からidを選択し、さらに使用します。これはおそらく最も簡単なケースですが、要素を見つける方法を提供します。ラベルがない場合ははるかに困難ですが、いくつかの要素を見つけて、兄弟、下降/上昇要素を探してxpathを記述する必要があります。
正式なHTMLではない複雑なUIの場合、xPathは常に信頼できるものですが、ExtJを使用した別のUI実装に関しては少し複雑です。
FirebugとFirexpathをfirefox拡張機能として使用して、特定の要素のxpathをテストし、Seleniumのパラメーターとして完全なxpathを渡すことができます。
たとえば、Java code:
String fullXpath = "xpath=//div[@id='mainDiv']//div[contains(@class,'x-grid-row')]//table/tbody/tr[1]/td[1]//button"
Selenium.click(fullXpath);