私たちはセレンを使用して、高レベルのWebサイトテスト(モジュールレベルでの広範なpython doctestに加えて)の処理に大きな成功を収めてきました。ただし、現在は多くのページにextjsを使用しており、グリッドなどの複雑なコンポーネントのSeleniumテストを組み込むのは困難です。
誰かがextjsベースのWebページの自動テストを書くことに成功しましたか?グーグルの多くは、同様の問題を持つ人を見つけますが、答えはほとんどありません。ありがとう!
私たちはセレンを使用して、高レベルのWebサイトテスト(モジュールレベルでの広範なpython doctestに加えて)の処理に大きな成功を収めてきました。ただし、現在は多くのページにextjsを使用しており、グリッドなどの複雑なコンポーネントのSeleniumテストを組み込むのは困難です。
誰かがextjsベースのWebページの自動テストを書くことに成功しましたか?グーグルの多くは、同様の問題を持つ人を見つけますが、答えはほとんどありません。ありがとう!
回答:
ExtJSをSeleniumでテストする際の最大のハードルは、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
フォームフィールド(Ext.form。*のコンポーネント)検証のための正規表現またはvtypesが関連付けられている特定の遅延(参照して検証をトリガーするvalidationDelay
ユーザーがテキストを入力するか、直後にフィールドを失ったとき、デフォルトでは250ミリ秒に設定されているプロパティ)フォーカス-またはぼかし(validateOnDelay
プロパティを参照)。type Seleniumコマンドを発行してフィールド内にテキストを入力した後でフィールド検証をトリガーするには、次のいずれかを実行する必要があります。
遅延検証のトリガー
フィールドがキーアップイベントを受け取ると、ExtJSは検証遅延タイマーを起動します。このタイマーをトリガーするには、ダミーのキーアップイベントを発行するだけです(ExtJSが無視するため、どのキーを使用してもかまいません)。次に、validationDelayよりも長い短い一時停止を続けます。
Command: keyUp
Target: someTextArea
Value: x
Command: pause
Target: 500
即時検証のトリガー
フィールドにぼかしイベントを挿入して、すぐに検証をトリガーできます。
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"))]
エラーフィールドが表示されてから非表示にする必要があるため、「display:none」チェックが必要であることに注意してください。ExtJSは、エラーツリーをDOMツリーから完全に削除するのではなく、単に非表示にします。
オプション1
コマンド:ターゲットをクリック:css = button:contains( 'Save')
キャプションによってボタンを選択します
オプション2
コマンド:ターゲットをクリック:css =#save-options button
IDでボタンを選択します
Command: runScript
Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
最初に値を設定し、オブザーバーがいる場合は、selectイベントを明示的に発生させます。
このブログは私を大いに助けてくれました。彼はそのトピックについてかなりたくさん書いていて、それはまだアクティブなようです。男はまた、良いデザインを高く評価しているようです。
彼は基本的に、クエリを実行するためにJavaScriptを送信することと、Extアプリで内部的に行うのと同じ方法でExt.ComponentQuery.queryメソッドを使用してデータを取得することについて話します。そうすれば、xtypesとitemIdsを使用でき、狂った自動生成されたものを解析しようとする必要はありません。
私が見つかりました。この記事で特には非常に役立ちます。
ここでもう少し詳細なものを投稿するかもしれません-これを正しく行う方法について頭を動かそうとしています
ExtJs Webアプリケーションをセレンでテストしています。最大の問題の1つは、グリッドでアイテムを選択してそれを操作することでした。
このために、ヘルパーメソッドを記述しました(ExtJとの対話を容易にするための便利なメソッドのコレクションであるSeleniumExtJsUtilsクラス内):
/**
* 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グリッドのように複雑なHTMLが生成されるため、Ext JS Webページはテストが難しい場合があります。
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の詳細も記載されています。これも役立つ場合があります。
ページ上のグリッドの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;
}
itemIdは、オブジェクト参照が利用できない場合にコンポーネントへの参照を取得する代替方法として使用できます。Ext.getCmpでIDを使用する代わりに、itemIdまたはIDを取得する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を検索する必要はもうありません。
セレンを使用するテストフレームワークを開発しており、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");
}
形式的なHTMLではない複雑なUIの場合、xPathは常に信頼できるものですが、ExtJを使用した別のUI実装に関しては少し複雑です。
FirebugとFirexpathをfirefoxの拡張機能として使用して、特定の要素のxpathをテストし、完全なxpathをパラメーターとしてセレンに渡すだけです。
たとえば、Javaコードでは次のようになります。
String fullXpath = "xpath=//div[@id='mainDiv']//div[contains(@class,'x-grid-row')]//table/tbody/tr[1]/td[1]//button"
selenium.click(fullXpath);
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を記述する必要があります。