それらの最も重要なもの:
- 同じ要素を持つページのページオブジェクトのコードを再利用できない。
- 多数の要素を持つページの読みやすさとコードの可視性の欠如。
- 要素の類型化の欠如。
この投稿では、YandexでオープンソースHTML要素フレームワークを使用してこれらの問題を解決する方法を学習します。 これは、ページオブジェクトテンプレートの概念を拡張し、Webページ上の要素とのやり取りをシンプル、柔軟、便利にすることができます。
ほとんどの人はおそらくパターンに精通しているため、パターン自体とその原理の説明については説明しません。 誰かが彼と会っていない場合は、この投稿またはマスタークラスから彼について知ることができます。 また、Page Objectパターンの使用について言えば、Selenium WebDriverフレームワークでのJava実装を意味します 。
コードの再利用
別のページではなく、Webサービス全体でテストを記述する必要があると想像してください。 そのページには、ヘッダー、フッター、場合によっては同一のフォームなど、共通の要素ブロックがおそらく見つかるでしょう。 たとえば、Yandexのメインページには検索フォームがあり、検索結果のあるページに移動すると保存されます。
また、他のYandexサービス(Yandex.Auto、Yandex.Market、Yandex.Workなど)にもあります。
承認フォームは、メインページだけでなく、たとえば、Yandex.PassportページまたはYandex.Marketでも確認できます。 各ページの共通ブロックとの対話のロジックはまったく同じです。 ただし、これらのページのページオブジェクトを記述する必要がある場合、これらのブロックとの相互作用を実装するコードをそれぞれのページで複製する必要があります。
あなたはおそらく私が何を得ているのかすでに理解していますか? はい、要素のブロックとそれらとの相互作用のロジックを別々に記述し、それらからページオブジェクトを既にアセンブルできるのは素晴らしいことです。 また、HTML Elementsフレームワークを使用すると、これを行うことができます。 たとえば、検索フォームの説明に使用します。
@Block(@FindBy(className = "b-head-search")) public class SearchArrow extends HtmlElement { @FindBy(name = "text") private WebElement requestInput; @FindBy(xpath = "//input[@type='submit']") private WebElement searchButton; public void search(String request) { requestInput.sendKeys(request); searchButton.click(); } }
承認フォームと同様に:
@Block(@FindBy(className = "b-domik__form")) public class AuthorizationForm extends HtmlElement { @FindBy(id = "b-domik-username") WebElement loginField; @FindBy(id = "b-domik-password") WebElement passwordField; @FindBy(xpath = "//input[@type='submit']") WebElement submitButton; public void login(String login, String password) { loginField.sendKeys(login); passwordField.sendKeys(password); submitButton.click(); } }
次に、Yandexメインページのページオブジェクトは次のようになります。
public class SearchPage { private SearchArrow searchArrow; private AuthorizationForm authorizationForm; // Other blocks and elements here public SearchPage(WebDriver driver) { HtmlElementLoader.populatePageObject(this, driver); } public void search(String request) { searchArrow.search(request); } public void login(String login, String password) { authorizationForm.login(login, password); } // Other methods here }
ところで、ブロック要素セレクターはブロックセレクター自体に対して相対的に設定されていることに気づきましたか? ブロックは異なるセレクターによって異なるページに配置できるため、これは非常に便利です。 この場合、ブロックの内部構造は変更されません。 この場合、ページオブジェクトにブロックを含めると、ブロック自体のセレクターをオーバーロードするだけで十分です。 たとえば、Yandex.Autoサービスのページでは、検索フォームはメインページとは異なる方法で検索する必要があります。
public class AutoHomePage { @FindBy(className = "b-search") private SearchArrow searchArrow; // Other blocks and elements here public AutoHomePage(WebDriver driver) { HtmlElementLoader.populatePageObject(this, driver); } public void search(String request) { searchArrow.search(request); } // Other methods here }
読みやすさと可視性
Webサービスの特定のページをテストで完全にカバーするには、そのすべての要素を使用する必要があります。 そして、それらはたくさんあります。 たとえば、Yandex.Avtoのメインページには、パラメーターで車を検索するためのフォームがあります。 高度な検索のほか、自動車ブランドのリスト、ニュースブロック、自動車のイノベーションのブロックなどを考慮して、30以上の要素があります。
Selenium WebDriverフレームワークの機能のみを使用してこのページのページオブジェクトを作成すると、要素の長いキャンバスとこれらすべての要素との相互作用を実装する多数のメソッドを持つ非常に大きなクラスを取得します。 同意して、そのようなクラスは非常に愛され、読みにくいでしょう。
しかし、要素のブロックを個別に作成する機会があれば、この問題も解決されます。 ページオブジェクトには数個のブロックのみが含まれ、それらの構造とそれらとの相互作用のロジックは個別に説明されます。
要素を入力する
Selenium WebDriverでは、ボタン、チェックボックス、テキスト入力フィールドなど、すべてのページ要素はWebElementインターフェイスを使用して記述されます。 したがって、さまざまなタイプの要素に特有の多くのメソッドがあります。 しかし、たとえば、ボタンと対話する場合、そこでテキストを操作したくないでしょう。
一方、ページには多くの場合、複雑な要素が含まれており、WebElementだけでは相互作用を説明できません。 ラジオボタンのグループ、ドロップダウンリスト、または日付ピッカーを考えてみましょう。
どちらの場合も、同じ解決策がそれを示唆しています。最初の場合はWebElement'aインターフェースを狭めるタイプされた要素を導入し、2番目はより複雑な要素との相互作用を実装します。 これは、HTML Elementsフレームワークで行ったことです。
たとえば、入力された要素を使用した検索フォームの説明は次のようになります。
@Block(@FindBy(className = "b-head-search")) public class SearchArrow extends HtmlElement { @FindBy(name = "text") private TextInput requestInput; @FindBy(xpath = "//input[@type='submit']") private Button searchButton; public void search(String request) { requestInput.sendKeys(request); searchButton.click(); } }
そのため、ドロップダウンリストがある検索設定で言語を選択するためのフォームの説明は次のようになります。
@Block(@FindBy(id = "lang")) public class LanguageSelectionForm extends HtmlElement { @FindBy(className = "b-form__select") private Select listOfLanguages; @FindBy(xpath = "//input[@type='submit']") private Button saveButton; @FindBy(xpath = "//input[@type='button']") private Button returnButton; public void selectLanguage(String language) { listOfLanguages.selectByValue(language); saveButton.click(); } }
TextInput、Button、CheckBox、Select、Radio、Linkなどの基本要素のサポートを既に実装しています。 あなたも非常に簡単に独自の型付き要素を記述し、既存の要素を拡張できます。
***
HTML Elementsフレームワークは、コンストラクターとしてページオブジェクトをアセンブルできるツールです。 入力された要素から、必要なブロックを収集できます。これらのブロックを結合、相互に結合し、そこからページオブジェクトを組み立てることができます。 これにより、コードの再利用の度合いが大幅に向上し、読みやすく直感的になり、テストの作成が容易になります。 HTML Elementsはオープンソースで利用可能です。 試してみて、 GitHubでコードを確認してください。
Yandexでのテストに関する次の投稿の1つで、フレームワーク自体について詳しく説明します。 他にどんな便利な機能があり、その助けを借りてWebインターフェースをテストすることがどのように便利であるかがわかります。