Selenium WebDriverWaitの適切性について

Selenium WebDriverを詳しく知るほど、この機能またはその機能が他の方法ではなく、このように実装されている理由がよくわかります。 Teleubles in Selenium WebDriver」で、 Alexey Barantsevはこの自動化ツールを実装することの微妙さを明らかにし、「バグ」と「機能」を区別しています。 ビデオには多くの興味深いものがありますが、それでも日陰には(少なくとも私にとっては)いくつかのポイントが残っています。



この記事では、 WebDriverWaitクラスとそのメインのUntilメソッドを使用して実装された、ページ上のイベントを待機するために頻繁に使用されるツールについて説明します。 WebDriverWaitが必要なのかどうか、それを拒否することは可能ですか?



リフレクションはC#のコンテキストで表示されますが、このクラスの実装ロジックは他のプログラミング言語とは異なるとは思いません。



WebDriverWaitのインスタンスを作成すると、ドライバーインスタンスはコンストラクターに渡され、コンストラクターは内部入力フィールドに格納されます。 Untilメソッドは、入力パラメーターがIWebDriverである入力をインスタンスとするデリゲートを想定しています。



Untilメソッドのソースコードを見てみましょう。 彼のロジックのバックボーンは、終了するための2つの条件を持つ無限のサイクルです:目的のイベントの開始またはタイムアウト。 追加の「グッズ」は、定義済みの例外を無視し、besがTResultとして機能しない場合はオブジェクトを返します(詳細は後ほど)。



最初の制限は、IWebDriverのインスタンスが常に必要なことです。ただし、Untilメソッド内(厳密には条件の入力パラメーターとして)では、ISearchContextを完全にバイパスできます。 実際、ほとんどの場合、一部の要素またはそのプロパティの変更を予期し、FindElementを使用して検索します。



クライアントコード(クラス)はページオブジェクトだけではなく、子の検索でページルートから反発されるため、ISearchContextを使用することはさらに論理的だと思います。 これは、ページ自体ではなく、別のページ要素をルートとする複合要素を記述するクラスである場合があります。 この例はSelectElementで、コンストラクターで親IWebElementへのリンクを受け入れます。



WebDriverWaitの初期化の問題に戻りましょう。 このアクションには、ドライバーのインスタンスが必要です。 つまり 何らかの方法で、すでに「親」を受け入れる複合要素(SelectElementの例)のクラスであっても、クライアントコードの外部からIWebDriverのインスタンスをスローする必要があります。 私の観点から、これは不要です。



もちろん、類推によってクラスを宣言できます
SearchContextWait : DefaultWait<ISearchContext>
      
      



しかし、急いではいけません。 彼は必要ありません。



conditionに渡されたドライバーインスタンスがどのように使用されるかを見てみましょう。 通常、次のようになります。



 var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); wait.Until( d => d.FindElements(By.XPath("locator")).Count > 0 );
      
      





問題が発生します:条件が常にクライアントコードから利用できる場合、ドライバーの「ローカル」バージョンが必要なのはなぜですか? さらに、これはコンストラクターを介して以前に渡された同じインスタンスです。 つまり コードは次のようになります。



 var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); wait.Until( d => Driver.FindElements(By.XPath("locator")).Count > 0 );
      
      





サイモンスチュワートでさえ、彼の演説でこのアプローチを使用しています。



画像



彼は「d-> d」とは書いていませんが、「d-> driver。」と書いています。 メソッドに渡されるドライバーインスタンスは単に無視されます。 しかし、それを送信する必要があります。これはメソッドの署名に必要なためです!



メソッドの条件内でドライバーを渡すのはなぜですか? ExpectedConditionsで実装されているように、このメソッド内で検索を分離することは可能ですか? TextToBePresentInElementメソッドの実装を見てください。 またはVisibilityOfAllElementsLocatedBy 。 またはTextToBePresentInElementValue 。 転送されたドライバーはそれらでも使用されていません!



そのため、最初に考えられるのは、ドライバーが受け入れるデリゲートパラメーターを持つUntilメソッドは必要ないということです。



それでは、Untilメソッドに戻り値が必要かどうかを考えてみましょう。 boolがTResultとして機能する場合、いいえ、必要ありません。 実際、 成功した場合は trueになり、失敗した場合は TimeoutExceptionが発生します。 そのような行動の情報内容は何ですか?



しかし、オブジェクトがTResultである場合はどうでしょうか? 次の構成を想定します。



 var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); wait.IgnoreExceptionTypes(typeof(NoSuchElementException)); var element = wait.Until(d => d.FindElement(By.XPath("locator")));
      
      





つまり 要素が表示されるのを待つだけでなく、それを使用して(待機している場合)、DOMへの不要な呼び出しを1つ削除します。 いいね



これらの3行のコードを詳しく見てみましょう。 Untilメソッドの実装内では、これは一種の類似性(条件コード)に要約されます。



 try { FindElement } catch (NoSuchElementException) {}
      
      





つまり 要素がDOMに表示されるまで、例外が毎回スローされます。 例外の生成はかなり高価なイベントであるため、特に難しくない場所では例外を避けることをお勧めします。 次のようにコードを書き換えることができます。



 var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); var elements = wait.Until(d => d.FindElements(By.XPath("locator")));
      
      





つまり 例外をスローしないFindElementsを使用します。 待って、このデザインは要素が現れるのを待ちますか? いや! なぜなら、 ソースコードを見ると、条件がnull以外を返すとすぐに無限ループがすぐに完了するからです。 また、失敗した場合のFindElementsは空のコレクションを返しますが、nullは返しません。 つまり アイテムのリストについては、Untilを使用しても意味がありません。



わかりました、リストは明確です。 それでも、見つかった要素を返し、例外をスローしない方法は? コードは次のようになります。



 var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); var element = wait.Until(d => d.FindElements(By.XPath("locator")).FirstOrDefault());
      
      





この場合、ループの各反復で、IWebElementリスト(空の場合もある)を取得するだけでなく、そこから最初の要素を抽出しようとします。 要素がまだページに表示されない場合は、null(オブジェクトのデフォルト値)を取得し、ループの次の反復に進みます。 要素が見つかった場合、メソッドを終了し、要素変数は戻り値で初期化されます。



それでも、考え直されました-ほとんどの場合、Untilメソッドの戻り値は使用されません。



渡された値は不要であり、戻り値は使用されません。 untilの有用性は何ですか? 条件メソッドを呼び出すサイクルと頻度でのみ? このアプローチは、 SpinWait.SpinUntilメソッドのC#で既に実装されています。 唯一の違いは、タイムアウト例外をスローしないことです。 これは次のように修正できます。



 public void Wait(Func<bool> condition, TimeSpan timeout) { var waited = SpinWait.SpinUntil(condition, timeout); if (!waited) { throw new TimeoutException(); } }
      
      





つまり ほとんどの場合、これらの数行のコードはWebDriverWaitクラス全体のロジックを置き換えます。 その努力は結果に値するものですか?



更新する



記事へのコメントで、 KSAユーザーは条件の実行頻度に関してSpinUntilとUntilの違いについて賢明な発言をしました。 WebDriverWaitの場合、この値は調整可能で 、デフォルトは500ミリ秒です。 つまり Untilメソッドには、ループの反復間の遅延があります。 SpinUntilの場合、 ロジックは少し複雑であり、多くの場合、待機は1ミリ秒を超えません。



実際には、これにより、2秒以内に表示される要素を待機している間に、Unitlメソッドが4回の反復を実行し、SpinUntilメソッドが約200以上を要する状況になります。



SpinUntilを放棄して、Waitメソッドを次のように書き換えましょう。



 public void Wait(Func<bool> condition, TimeSpan timeout, int evaluatedInterval = 500) { Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed < timeout) { if (condition()) { return; } Thread.Sleep(evaluatedInterval); } throw new TimeoutException(); }
      
      







数行のコードを追加すると同時に、Untilメソッドのロジックに近づきました。




All Articles