セレンのスケーリング

Seleniumを使用したテストが1つだけあるとします。 何が不安定になりますか? スピードアップする方法は? 次に、2つのテストがあることを想像してください。 今百を想像してください。 このような一連のテストをすばやく解決する方法は? テストの数が増え続けるとどうなりますか?







この記事では、Simon Stewartが、1つのテストから数百のテストを並行して実行するという、難しいスケーリングの方法を説明します。 この場合に発生する問題、およびこれらの問題を解決するための実用的な方法について知ります。 Javaコードと、テストインフラストラクチャの開発に関するいくつかの考えがあります。









この記事のプロトタイプは、モスクワのハイゼンバグ2017でのSimon Stewartによるレポートです。 サイモンはWebDriverの作成者です。WebDriverは、ほぼ11年前の技術です。 彼は約9年前にSeleniumプロジェクトマネージャーになりました。 Googleで、彼はインフラストラクチャ上で毎日数万から数百万のテストにSeleniumを拡張することに従事していました。 それからFacebookに行きました。 彼は現在、W3Cテストおよび調整グループの一部であるW3CのWebDriver仕様を開発しています。 WebDriverに基づいて、標準が作成されていると言えます。







記事の過程で、簡単なテストを検討し、それをどのようにスケーリングできるかを示したいと思います。 まず、パーソナルラップトップで起動し、最終的にはクラウドで動作し、周囲のすべての人の想像力を驚かします。 上司は、あごを傷つけて「私は明らかにあなたに十分なお金を払っていない」と言うでしょう 。 このためにソフトウェアを作成します。







まず、決定しましょう-なぜテストが必要なのですか? 私たちは緑(または赤)の色が好きだからではなく、私たちの作品が好きだからでもありません。 唯一の機能は、ソフトウェアが意図したとおりに機能することを確認することです。 エンドツーエンドのテスト(例:セレン)は、バランスの取れたテストダイエットの一部である必要があります。 それら以外に何も消費されなければ、それから良いものは何も生まれません。 ただし、これについては後で説明します。









IDEで開かれている簡単な例から始めましょう。 これはlongAndWrong()



と呼ばれ、これがテストの方法です。 実行がローカルで実行されるように、新しいFirefoxDriver



を作成しています。 次に、 WebDriverWait



明示的な待機を作成します。 その後、 http://localhost:8080



に移動し、電子メールアドレスとパスワードを入力し、「todoの作成」要素が作成されるまで待って、最後にクリックします。 誰もがSeleniumのリリースを見てきましたが、この特定のコードに異常はありません。







このテストはひどいです。 理由を見てみましょう。 まず第一に、彼は幸運のために働いています。 ページを受け取った後、入力が行われる要素が表示されるまで待機しません。 したがって、WebDriverがページのロード時間を正しく推測することが期待されます。 上記の例では、HTMLが表示されるだけなので、このアプローチは機能しますが、他の場合には問題が発生します。







さらに重要なのは、コードの別のセクションで見る狂気です: driver.findElements(By.tagName("button")).stream()



など。 フィルタリングが発生し、何も見つからない場合は、何らかの理由でAssertionError



がスローされます。 これらすべての操作がクリックされた後にのみ、必要なものがすべて揃っていることが明らかになるためです。 それは非常に不気味に見えることに誰もが同意しますか?











これは、緑色で強調表示されています-それは何ですか? この構造全体の問題は、その脆弱性です。 そしてこれだけではありません。 たとえば、過度に長いXPathを使用している人はいますか? Longは、1行より長いことを意味します。 壊れやすいロケーターは、テストが不気味になる主な理由の1つです。 Selenideレポートでこの問題の解決について多くを学ぶことができます。







状況を改善するにはいくつかの方法があります。 まず、ソースコードにアクセスし、それを編集する権限がある場合、テスト済みのアプリケーション自体を正しく書き換えることができます。 彼はいつもそこにいるわけではありません。 英国の開発チームが刑務所のチケットを介してルーマニアのテストチームと通信することもあります。また、アプリケーションが「正常に」動作する一方で、テストに合格しないと言う人もいます。 コードにアクセスできる場合、意味のある識別子を要素に追加できます:クラス、特定の属性。









おそらく、単一のBy



入力を受け入れるWebDriver.findElement



ようなものがあります。 手動で継承できます。 セレクタ*



ですべての要素を探し、ツリー内のすべての子要素を探し、属性の値でフィルタリングします。 ただし、これは非常に非効率的です。 WebDriverまたはSelenium APIへの各呼び出しは、リモートプロシージャへの呼び出しであり、何らかの方法でネットワークを介して行われます。









代わりに、これらはすべてJavaScriptで記述でき、まったく同じことを行います。 スライドを見ると、はるかに複雑で怖いように見えますが、本質的には、コンテキストを取り、尋ねます-あなたは本当にWebDriverですか? 次に、ラップされたWebDriver



そこから抽出できます。 また、JavaScriptを実行するため、さらにJavaScriptをJavascriptExecutor



キャストします。 ささいなことなど。 誰かが知らないかもしれませんが、 executeScript()



は要素やその他のさまざまなものを返すことができます。 ブラウザに移動し、そこで何らかの作業を行い、結果を返します。結果はJava型に正しくキャストされます。 画面に表示されるものは実際に機能します。







一部の開発者は、既に実装されていることを知らずに、そのような機能を夢見ています。 既に要素検索エンジンが組み込まれているJSフレームワークがあるとします。 ランダムIDの生成など、単純なものであっても:-)このメカニズムを再実装することはできません。 必要な要素を見つけるためにフレームワーク自体に気軽に尋ねてください! これにより、生活が大幅に簡素化されます。







また、SeleniumがJavaScriptを使用する理由も追加します。 どの読者がアプリケーションでJavaScriptフレームワークを使用していますか? JQuery、React、Angular、または自家製の悪夢? ここでそれらとの対話には、JavaScriptも使用する必要があります。 私が引用した例では、jQueryはクエリの数を追跡しました。 システムで何が起こっているのかについて、システムから明確な情報を取得する他の方法はありません。 時にはそれが必要です。 さらに、WebDriverはユーザーの動作をシミュレートしようとします。 ユーザーは何をしますか? 彼はブラウザをクリックし、印刷し、要素をクリックします。 WebDriverとSeleniumにはできないことがあります。 HTTPステータスコードまたはネットワークトラフィックを監視する場合は、プロキシが必要になる場合があります。 ページで何かが発生した場合、最良のオプションはページ自体に尋ねることです。 たとえば、かなり頻繁に識別子がランダムに作成されますが、それらは整然と使用されます。 そのため、常にその可用性を当てにすることはできません。 ページに移動して、要素がどの識別子であるかを確認し、通常のインデックスで使用できます。 このメカニズム全体により、グレーボックステストを実行できます。 「ホワイトボックス」とは、テスト中に内部のシステムに完全にアクセスできる場合です。「ブラックボックス」の状況では、システムはまるで宇宙からやって来たかのように密閉されます。 また、「グレーボックス」をテストするときは、最初は内部のすべてが複雑であることに頭をつかんでから、ここで何かを変更し始め、そこにハンドラーを挿入します。 誰かがそれをハックと見なします。 ラリーウォールは、ファーストクラスの開発者には、イライラ、,慢、怠lazという3つの品質が必要だと考えています。 焦りはすべてが今起こることを必要とします。 これにより、プログラムは高速になります。 あなたが何度も何度も何かをするように求められた場合、あなたはこれをしないでしょう、これのための車があります。 怠け者は、一度は何度も働く準備ができているので、二度と仕事をすることはありません。 そのため、説明したアプローチでは、ハックではなく怠が見えます。 私はそれがどのように機能するかを理解しようとすることができます-または、これについて誰かに尋ねることができます。 私の人生は楽になります。 まあ、ar慢は散財したいだけです。







別のトピックは、イベントの予想です。 Selenium- Wait<?>



はそのようなことがありWait<?>











誰もが彼女に精通していることを願っています。 Seleniumには何かを待つ方法が2つあります。明示的と暗黙的です。 暗黙のうちに、私たちは何らかの不思議な時間を、明示的に待っています-あなたが今見ているものを使用します。 Seleniumチームからのアドバイス:暗黙の期待を使用せず、 Wait.until



使用してWait.until



!..なぜですか? 問題は、原則として、人々は待機する時間を知らないため、暗黙の待機時間を最大1分に設定することです。 すべてが正常である場合-これは問題ではなく、すべてが正常です。 ただし、テストが失敗した場合、停止するまでに1分かかります。 これにより、通常5〜15分かかるテストの起動に数時間かかることがあります。







明示的な待機時間が暗黙的な待機よりも短い場合、本質的には暗黙的な待機の結果のみを処理します。 これは非常に紛らわしく、そのようなテストをサポートすることは不可能です。 しかし、それでもできます。







はい、明示的な期待は暗黙的な期待と非常に奇妙に相互作用します。 奇妙な-「予測不可能」を意味しません。 実際、すべてが完全に予測可能です。 暗黙の待機時間を設定するコマンドが実行された場合、タイムアウトになるまで実行は終了しません。 暗黙の待機時間が10秒であり、明示的な待機時間が15であるとします。10秒後に失敗したことを報告する要求を実行します。 次に、明示的な待機は10と15を比較して、15の方が大きいと判断し、再び10秒間新しい待機を実行します。 頭を悩ませているのに、15と尋ねたら20秒待つのはなぜですか? 暗黙の期待がいつ開始されるかは必ずしも明確ではありません。 したがって、すべてが予想どおりに行われますが、この内部メカニズムを知らない場合、外部からこの動作は非常に奇妙に見え、あなたの人生は非常に困難になります。 私のアドバイスは、暗黙の期待をまったく使用しないことです。 明示的な期待には特定の情報が含まれます。 テストスイートはコードをチェックするだけでなく、コードに不慣れな人のためにシステムがどのように機能するかを記述します。 たとえば、明示的な期待は、現時点では、インターネット接続が発生する必要がある、AJAX呼び出しを行う必要がある、何かを更新する必要がある、などです。テストを読んでいる人が尋ねるかもしれません。 彼女はこれをすべきですか? なぜ彼女はこれをしているのですか? これにより、異なるアプローチでは不可能な対話を行うことができます。 一般に、明示的な期待と暗黙的な期待は相互に作用し、必ずしも明白な方法ではありませんが、暗黙的な期待は常に優先事項のように見えます。







元のテストに戻ります。









彼はどこでも寝ていません。 素朴なアプローチは、ある時点で待機する必要がある場合Thread.sleep()



実行するだけです。 このアプローチの問題は、テストの待機時間が長すぎることです。 これは必要ありません。







代わりに、 Wait



クラスを使用してWait



。 その利点は、ジェネリックを使用するため、入力でスローする型がuntil()



スローさuntil()



ことです。 たとえば、 Wait.until(d -> driver.findElement(...))



と書くことができます。さらに要素をスローするuntil



要素を見つけます。そこから.isDisplayed()



を呼び出すと非常に便利です。







さらに、WebElementへのリンクを保存すると便利な場合がありますが、これは何らかの理由で回避されます。 私はかなり多くのクライアントとコミュニケーションを取り、彼らのサイトにアクセスし、要素とのやり取りごとに再び探していることに気付きます。 したがって、各アクション呼び出しに対して、2つのリモート呼び出しが行われます。 値を取得してキーを送信する必要があるとします。 この場合、多くの場合、最初にdriver.switchTo().activeElement().clear()



driver.switchTo().activeElement().sendKeys()



、次にdriver.switchTo().activeElement().sendKeys()



ます。







しかし、要素は同じです。 変更できる唯一の状況は、DOMから完全に削除または切断された場合です。この場合、 StaleElementReferenceException



ます。 皆さんはこの例外に夢中ではないですか? 何かがDOMを更新し、アイテムがなくなったことを報告します。 これは、待機を設定する機会が失われていることを意味します。目的のアイテムが表示されるまで待機することはできません。









画面に表示されるコードは、待機時間が最小化されるため、最適な時間を実行します。 このコードでテストを再開すると、結果はまったく変わりません。







まだ読んでいますか? これまでのところ、新しいことは何も言っていないことを願っています:-)







だから、グレーボックステスト。









期待をより効果的にする方法があります。 上記のスライドのisJqueryDone()



メソッドを見てください。 彼はアクティブな操作の記録を保持します。 jQuery.active



が0になると、他に何も起きていないことが明らかになります。







一方で、なぜこれらの統計を見つけるために常にページをプルするのですか? 結局、JSフレームワークには同様のメカニズムがあります。 たとえば、AngularJSの場合。 なぜこれだけに制限できないのですか? おそらく、ライブラリは単に適切なレベルではありません。







それでも、問題はアプリケーションを使用して解決できます。 AJAX呼び出しを行っており、それが完了したかどうかを知りたいとします。 時々、DOMは更新されず、新しいコンテンツが単純にそこにスローされます。 テストを続行できるかどうかが不明確になります。 たぶんすべてが同じままで、テストを続行できますか? このような状況では、アプリケーションレベルで監視する価値があります。意味のある操作を行うときは、変数を作成する必要があり、操作が完了した後、その値をリセットできます。 その後、この変数をチェックすることが可能になります。正しい値をとると、さらにテストを安全に続行できることを意味します。







最後に、 Seleniumに支援を求めることができます。 廃止されたアイテムへの参照は、役に立つ情報になる場合があります。 アクションの結果として、DOM要素が更新され、DOMから削除されることが予想される場合、このアクションが実行される前に要素へのリンクを取得できます。 その後、 StaleElementReference



例外を待機し、新しい要素を見つけて返すことができます。 Wait



でいくつかの種類の例外を無視できるため、このコードは簡潔で整然としており、使いやすいです。 セレンはいくつかの仕事を引き受けます。 DOMの変更、つまり何かが内部で動いたことを示すシグナルは、テストをより安定させる素晴らしい機会です。







ページオブジェクトに移りましょう。 古代ローマ神話からヤヌス神について聞いたことがありますか?









ヤヌスは過去と未来を見る双頭の神です。 1月が彼にちなんで名付けられた理由です。







ページオブジェクトは多くの場合、誤解の犠牲になります。 SeleniumConfのスピーチでは、PageObjectsの自動生成、要素の自動配置に関するプレゼンテーションがたくさんありました。 これはできません 「フレームワークを書く」ように見えて、あなたは素晴らしく、実際にはすべてがもっと悪くなるので、それは美しく見えるだけです。







元の定義では、Page ObjectはJanusの顔の1つです。 これらはユーザー向けのサービスです。 テスト対象のアプリケーションにログインページがある場合は、ログインページにログインして、サブジェクトエリアの言語でこれらすべてを説明します。 このようなテストをビジネス分析、プロジェクトオーナー、両親に提示すると、アプリケーションが正常に動作しているかどうかがすぐに明らかになります。 しかし、Page Objectには、コードとページ構造の深い知識を必要とするJanusの別の顔があります。 これは、DRY(「繰り返さないでください」)、抽象化に必要です。 LoadableComponent



簡単にするために、SeleniumはLoadableComponent



クラスを提供します。 Page Objectという名前は本質をあまり反映していないようです。なぜなら、ここでは小さなピースでシミュレートでき、意図的にサイズを小さくできるからです。









ここではページオブジェクトを使用したテストがあります。 前のテストとまったく同じです。 User



オブジェクトを作成し、ログインページに移動してdriver



と最大タイムアウトを渡し、 get()



を実行しget()



。 これは、 LoadableComponent



モデルに従って発生します。 ログインすると、メインページに戻ります。 このテストでページオブジェクトテンプレートを実装する方法の利点は、ナビゲートできることです。 ログインページがMainPageをスローしなくなった場合、関数の署名を変更し、signUpメソッドから何か他のものを返します。 そのようなテストは実行する必要さえなく、単にコンパイルしません。







デモを見てみましょう:









これは簡単なToDoリストです。 それは驚くべきことではありませんが、アイデアをよく示しています。









SignupPage.signUp()



関数のコードでは、要素の検索のみが発生します。









すべてが抽象化され、この1つの関数に配置されます。 ログインページのコードが変更された場合、この関数は修正が必要な唯一の場所です。 開発者がUIワークフローを変更したか、一部の要素の名前を変更した場合、すべての変更はここにあります。 別の方法は、何百万ものテストを実行して修正することです。







これで基本は終わりです。 サポートの実装が簡単なテストがあります。 Seleniumを拡張する1つの方法は、適切に実行され、簡単に保守できるテストを記述することです。 かつて私は、彼らの仕事はテストスイートをグリーンにすることだと言われたクライアントと会いました。 すべてのチェックを削除することでこの問題を解決し、例外が発生した場合はテストを正常に完了しました。 一般にテストは成功しましたが、何の意味もありませんでした。







また、使用されるデータは非常に重要です。 アプリケーションが完全にステートレスである可能性はほとんどありません。 ほとんどの場合、ユーザー、永続データなどがあります。 データは、テストをスケーリングするときに発生する問題の1つです。 テストを並列化する方法に関するいくつかの推奨事項があります。









まず、Javaのstatic



およびシングルトンのデザインパターンは悪であり、避けるべきです。 その理由は、静的フィールドはすべてのスレッドに共通しているためです。 このフィールドを2つのテストから同時に変更すると、結果は予測できなくなります。 クライアントと通信するときに、静的変数を使用してWebDriverへのリンクを保存することがよくあります。 テストが並行して実行される場合のテストのクレイジーな動作について不満があります。時には動作する場合と動作しない場合があります。 これは「競合状態」と呼ばれます。







static



を避けようとして、 ThreadLocal



使用に切り替えます。 しかし、彼も悪です。 スレッドアフィニティに依存するようになります。 テストが同じスレッドで実行されることを常に確信している場合、レベル間でデータを安全にテレポートできます。 データ(WebDriverインスタンス、ユーザー名)をテレポートする必要があるという事実は、すでに悪い兆候であり、コードのにおいです。 したがって、それらは不十分な構造です。 それらは理解するのが難しく、維持するのが難しいです。 理解するのが難しいテストは、大きなトラブルの原因です。 ある同僚は、テストをデバッグするには、書くよりも2倍の知性が必要だと言いました。 テストを書くためにすべての能力を使用しなければならなかった場合、デバッグ中に行き止まりに陥り、そこから抜け出すことは非常に苦痛になります。 Javaを含め、関数型プログラミングが教えてくれたことの1つ:理想的なコードは不変でなければならず、ステートレスであってはなりません。 何かを変更するために(前の図のように) Todo



オブジェクトを作成する場合、新しいオブジェクトを作成する必要があります。 可変状態で同時に動作する2つのスレッドがある場合、これはひどい結果につながります。







このテストを想像してください。ユーザーFredがexample.comにアクセスしてログインすると、登録が成功する必要があります。 テストが初めて成功したとき。 ただし、テストを再度実行すると、Fredという名前のユーザーが既に登録されているため、テストがクラッシュします。 不快ですね。 私はあなたがそのようなものに常に会うと確信しています。 適切なアプローチは、各テストでデータを特別に準備することです。









テスト実行の間に環境を再作成するため、この問題は一般的ではありません。 , CI/CD ( ). ( , ), , .







, . — «» .









- , , . . dev , , . . , ! , happy path.







dev . : - ( - - , todo), , . , , , , . , , . — . , «».







-.









, . , LDAP. . , — , LDAP, . , . , , , . , . , , . , — , . , , - . . , . , JUnit Assume



. .







, . , , . , , , . , . , . , , , — « ?», — « , ». . , , . , .







, , , , . Page Objects , , Screenplay, . , .







Selenium?









WebDriver Wire. , JSON- URL. : Json Wire Protocol W3C Dialect. OSS Dialect. , ChromeDriver, FirefoxDriver, Selenium Server, PhantomJS. W3C — Wire, . . — , , — Actions. Selenium Grid FirefoxDriver, drag-n-drop. . .







Selenium: .









. Selenium Grid. :









Selenium . :









, . . , , , . : Firefox, Chrome , , macOS — Safari:









, ChromeDriver, GeckoDriver, , . : ? Docker . — -!







, ? , , . . Docker , , — .







, Selenium , . — Selenium Docker . , . , GeckoDriver ChromeDriver, Firefox GeckoDriver . . Selenium- , Docker-.









( Chrome, — Firefox).









.









, Docker , . , . , . , 8 , . , , — .









, , . , Docker . Zalenium . .









Docker, :









. , . しかし、ニュアンスがあります。 Windows, , , Edge.









. . , Zalenium .









Zalenium --sauceLabsEnabled true



, Sauce Labs . , , Sauce Labs , .









*Zalenium*** .









Zalenium , /dashboard



. , . , . , , Windows macOS, Sauce Labs , BrowserStack - .







. , . , Selenium , , HTTP, . , , .







. — DDoS, , . , . , - . Selenium , . , , .









, — « ». , — ( «»), - ( «»). , , . , . , — - , — .







, : - .









, . , -. : Selenium-. Selenium — .







. . , . Buck Bazel , , . .







, . , , , — . , , . , , . JavaScript, . , ? , , , . . , .







広告の分。 おそらくご存知のように、会議を行っています。 — Heisenbug 2018 Piter , 17-18 2018 -. , ( — ), . , , !



All Articles