Selenium:すべてのAJAXリクエストが完了するのを待つ

最近、多くの異なるAJAXアプリケーションが離婚しました。 実際、このようなアプリケーションのテストの自動化は、通常のWEBアプリケーションのテストの自動化と違いはありませんが、いくつかの微妙な点があります。 微妙な点の1つは、すべてのAJAX要求が完了するのを待っていることです。 たとえば、ページ上の特定のチェックボックスのマークがAJAXリクエストによる選択の更新を引き起こす場合、マークの直後に特定のオプションを選択するテストは失敗します。 このオプションはありません。 すべては、テスト自体がリストを更新するAJAXリクエストよりもはるかに速いためです。



この場合、オートマトンにはいくつかの出力があります。



チェックボックスマークの後にスリープします。

これは最悪であり、残念ながら、最も一般的に使用されるソリューションです。

AJAXリクエストの実行にかかる時間は事前にわからないため、ほとんどの場合に十分な最小値に基づいて待機時間を設定する必要があります。 たとえば、5秒。 5秒間に多くのそのような期待がある場合、すべてのAJAX要求が迅速に実行された場合でも、テストは非常に長い時間実行され始めます。

また、さまざまな理由で、AJAXリクエストの実行時間が5.2秒になることがあります。そのような場合、誤ったテストエラーが発生しますが、これも悪いことです。



Waitクラスを使用して、目的のオプションに対してSelenium#isElementPresentがtrueを返すまで待ち​​ます。

この方法はすでに優れていますが、まだ適用すべきではありません。将来、その理由を詳しく説明します。 Waitクラスではなく、Selenium#waitForConditionメソッドを使用することをお勧めします。Waitクラスでは、目的の要素が表示されるまで待機します。



どういうわけか、チェックボックスをオンにした後、すべてのAJAXリクエストが完了するのを待ち、その選択オプションを選択します。

この方法をより詳細に検討します。 自動化の観点から見ると、非常に用途が広くシンプルです。



AJAXを操作するほとんどのWEBアプリケーションは、標準APIよりも高いレベルの抽象化を開発者に提供する特殊なライブラリ(jQuery、Prototype、Dojoなど)を使用するため、柔軟性が向上します。



SeleniumテストですべてのAJAXリクエストの完了を待つには、これらのリクエストをグローバルに監視する方法を学ぶ必要があります。 標準のAPIにグローバルインターセプターをインストールする可能性はありませんが、実際には、サードパーティのライブラリのそれぞれにそのような機会がありますが、どこでも独自の方法で行われます。 jQueryライブラリを使用するときに、すべてのAJAXリクエストが完了するまで待機する方法の例を次に示します。

/** * Waits for all active jQuery AJAX requests to finish. * * @param timeout Timeout in milliseconds. * @throws SeleniumError If timeout is reached. */ Selenium.prototype.doWaitForJqueryAjaxRequests = function(timeout) { return Selenium.decorateFunctionWithTimeout(function() { return selenium.browserbot.getUserWindow().jQuery.active == 0; }, timeout); };
      
      



ここでは、必要な条件(アクティブなAJAX要求の数が0)をSelenium#decorateFunctionWithTimeoutメソッドでラップします。このメソッドは、timeoutで指定されたタイムアウト内でこの条件が満たされるまで待機します。待機する場合、メソッドは正常に完了します。そうでない場合、SeleniumError例外がスローされます。



汎用の待機メソッドを作成するために必要なものをメタ言語で記述すると、次のようなものが得られます。

  1. AJAXの操作に使用するライブラリを決定します。
  2. 使用中の各ライブラリのすべてのAJAXリクエストが完了するまで待ちます。


簡単です。JavaScriptでこれを実装し、必要に応じてSelenium RCまたはSelenium IDEに拡張機能として接続します。 汎用性を高めるためにSelenium RCを使用する場合、DefaultSelenium#setExtensionJsメソッドを使用して拡張コードをロードできます。



完成した実装は次のとおりです(jQuery、Prototype、Dojoでサポートされています)。

 /** * Waits for all active AJAX requests to finish during specified timeout. Works only for AJAX requests which are * instantiated using one of the following frameworks: jQuery, Prototype, Dojo. Don't work (immediately returns without * any errors) if standard AJAX API or one of other frameworks is used to send XML HTTP request. * * @param timeout Timeout in milliseconds. * @throws SeleniumError If timeout is reached. */ Selenium.prototype.doWaitForAjaxRequests = function(timeout) { return Selenium.decorateFunctionWithTimeout(function() { var userWindow = selenium.browserbot.getUserWindow(); var isJqueryComplete = typeof(userWindow.jQuery) != 'function' || userWindow.jQuery.active == 0; var isPrototypeComplete = typeof(userWindow.Ajax) != 'function' || userWindow.Ajax.activeRequestCount == 0; var isDojoComplete = typeof(userWindow.dojo) != 'function' || userWindow.dojo.io.XMLHTTPTransport.inFlight.length == 0; return isJqueryComplete && isPrototypeComplete && isDojoComplete; }, timeout); };
      
      





Seleneseではなく、通常のプログラミング言語を使用してテストを記述する場合、新しいメソッドを使用できるようにするには、このメソッドを次のように追加して、使用するドライバーを拡張する必要があります。

 import com.thoughtworks.selenium.CommandProcessor; import com.thoughtworks.selenium.DefaultSelenium; public class CustomSelenium extends DefaultSelenium { public CustomSelenium(String serverHost, int serverPort, String browserStartCommand, String browserURL) { super(serverHost, serverPort, browserStartCommand, browserURL); } public CustomSelenium(CommandProcessor processor) { super(processor); } /** * Waits for all active AJAX requests to finish during specified timeout. Works only for AJAX requests which are * instantiated using one of the following frameworks: jQuery, Prototype, Dojo. Don't work (immediately returns * without any errors) if standard AJAX API is used to send request. * * @param timeout Timeout in milliseconds. */ public void waitForAjaxRequests(final int timeout) { commandProcessor.doCommand("waitForAjaxRequests", new String[]{String.valueOf(timeout)}); } }
      
      







これで、次のテストコードを簡単に置き換えることができます。

 ... selenium.check("name=enableBender"); sleep(5000); selenium.select("name=mode", "label=Kill all humans"); ...
      
      





これについて:

 ... selenium.check("name=enableBender"); selenium.waitForAjaxRequests(60000); selenium.select("name=mode", "label=Kill all humans"); ...
      
      





そして、テストはサーバーの応答速度に等しい速度で実行されます。 不要な遅延なし。



ページが読み込まれた直後にAJAXリクエストが開始されるプロジェクト(はい、いくつかあります)では、テストで絶えずプルされないように、最後の呼び出しでwaitForAjaxRequestsを追加して、waitForPageToLoad、waitForFrameToLoad、およびwaitForPopUpメソッドをオーバーロードすることをお勧めします。



最後に、標準APIにはAJAX要求のグローバルインターセプターをインストールする機能がないため、開発者が標準APIを直接使用する場合、このメソッドは機能しないことを繰り返します。 幸いなことに、多かれ少なかれ深刻なプロジェクトはそれをしません。 しかし、一部のプロジェクトでは独自のラッパーが標準APIの周りで使用される可能性があります。この場合、user-extensions.jsでこのラッパーをサポートする必要があるだけです。



All Articles