ブラウザーで extjs コードを、できれば Selenium を使用してテストするための提案はありますか?

StackOverflow https://stackoverflow.com/questions/107314

質問

私たちは Selenium を使用して、(モジュール レベルでの広範な Python doctest に加えて) 高レベルの Web サイト テストを処理することに大きな成功を収めてきました。しかし、現在では多くのページに 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 種類があります。

ID ロケーターを CSS または XPath ロケーターに置き換える

CSS ロケーターは「css=」で始まり、XPath ロケーターは「//」で始まります (「xpath=」プレフィックスはオプションです)。CSS ロケーターは冗長性が低く、読みやすいため、XPath ロケーターよりも優先されるべきです。ただし、CSS ロケーターでは対応できないため、XPath ロケーターを使用する必要がある場合があります。

JavaScriptの実行

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

セレンでは、 runScript コマンドを使用すると、上記の操作をより簡潔な形式で実行できます。

with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }

AJAX と遅いレンダリングへの対処

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 指示。これは、イベント リスナーが実際にはコンテナ上にあり、子要素上のマウス イベントを監視しており、最終的には親にバブルアップするためです。タブ コントロールは一例です。「a」タブをクリックするには、 mouseDown タブラベルのイベント:

Command: mouseDownAt
Target: css=.x-tab-strip-text:contains('Options')
Value: 0,0

フィールドの検証

検証用の正規表現または vtype が関連付けられているフォーム フィールド (Ext.form.* コンポーネント) は、一定の遅延を伴って検証をトリガーします (「 validationDelay ユーザーがテキストを入力した後、またはフィールドがフォーカスを失ったとき、またはぼやけた直後 (デフォルトでは 250 ミリ秒に設定されているプロパティ) validateOnDelay 財産)。type Selenium コマンドを発行してフィールド内にテキストを入力した後にフィールド検証をトリガーするには、次のいずれかを実行する必要があります。

  • 遅延検証のトリガー

    ExtJS は、フィールドがキーアップ イベントを受信すると、検証遅延タイマーを起動します。このタイマーをトリガーするには、ダミーの keyup イベント (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"))]

「表示:none」チェックが必要なのは、エラー フィールドが表示されてから非表示にする必要がある場合、ExtJS はエラー フィールドを DOM ツリーから完全に削除するのではなく、単に非表示にするだけです。

要素固有のヒント

Ext.form.Button のクリック

  • オプション1

    指示:ターゲットをクリックします:css=button:contains('保存')

    ボタンをキャプションで選択します

  • オプション 2

    指示:ターゲットをクリックします:css=#保存オプションボタン

    ID でボタンを選択します

Ext.form.ComboBox からの値の選択

Command: runScript
Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }

まず値を設定し、オブザーバーが存在する場合に備えて select イベントを明示的に起動します。

他のヒント

これ ブログ とても助けられました。彼はこのテーマに関してかなり多くの著作を執筆しており、まだ活動しているようです。この男はデザインの良さも気に入っているようだ。

彼は基本的に、JavaScript を送信してクエリを実行することと、Ext.ComponentQuery.query メソッドを使用して外部アプリの内部で行うのと同じ方法でデータを取得することについて話しています。そうすれば、xtypes と itemId を使用できるようになり、自動生成されたおかしなものを解析しようとする心配をする必要がなくなります。

見つけました この記事 特に非常に役に立ちます。

近いうちにもう少し詳しい内容をここに投稿するかもしれません - これを適切に行う方法をまだ考え中です

ExtJs Web アプリケーションを Selenium でテストしています。最大の問題の 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) );

これが機能するには、ExtJs に独自の ID を生成させずに、グリッドに自分の 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 ロボット では、動的ではない属性と条件に基づいてコンポーネントを確実に検索し、操作する方法に関する一連のベスト プラクティスを使用してこれに対処します。次に、操作する必要があるすべての HTML、Ext JS、および Sencha Touch コンポーネントでこれを行うためのショートカットが提供されます。2種類の味があります:

  1. ジャワ - 使い慣れた Selenium および JUnit ベースの API。最新のすべてのブラウザに対する Web ドライバーのサポートが組み込まれています。
  2. グウェン - ブラウザ テストを迅速かつ簡単に作成および保守するためのヒューマン スタイルの言語。独自の統合開発環境が付属しています。これらはすべて Java API に基づいています。

たとえば、テキスト「Foo」を含む Ext JS グリッド行を検索したい場合は、Java で次の操作を行うことができます。

findExtJsGridRow("Foo");

...そして、Gwen で次のことを行うことができます。

extjsgridrow by text "Foo"

どちらにも多くのドキュメントがあります ジャワ Ext JS 固有のコンポーネントの操作方法については 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;
    }

カスタム HTML データ属性によるテストの容易化

から 煎茶のドキュメント:

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 を検索する必要はもうありません。

私たちは 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");
}

正式な HTML ではない複雑な UI の場合、xPath は常に信頼できるものですが、ExtJ を使用したさまざまな UI 実装となると少し複雑になります。

Firebug と Firexpath を Firefox 拡張機能として使用して、特定の要素の xpath をテストし、完全な xpath をパラメータとして Selenium に渡すだけです。

たとえば、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)].

したがって、次からIDを選択します @for 属性を設定してさらに使用します。これはおそらく最も単純なケースですが、要素を見つける方法が得られます。ラベルがない場合は非常に困難ですが、要素を見つけて、兄弟要素、降順/昇順要素を探して xpath を記述する必要があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top