ページオブジェクト

selenideを利用する際に保守性の高いテストコードとしてとしてPageObjectデザインパターンがあります。

PageObjectは、画面を1つのオブジェクトとして作成し利用します。 画面を一つのオブジェクトとして切り出しておき利用しすることで対象画面に変更が入った場合にも対象の画面のみを修正することで他のテストケースへの影響を最小限とすることができます。 公式サイトにも記載がありますので確認してみたいと思います。

URL : http://selenide.org/documentation/page-objects.html

公式サイトにてサンプルとして記載されているのはgoogleの検索のページと検索結果のページをページオブジェクトとして作成しています。

検索ページ用のページオブジェクト

/**
 * 検索ページのページオブジェクトクラス
 */
public GoogleResultsPage search(String query) {
    $(By.name("q")).setValue(query).pressEnter();
    return page(GoogleResultsPage.class);
}

検索結果ページ用のページオブジェクト

/**
 * 検索結果のページオブジェクトクラス
 */
public static class GoogleResultsPage {
   public ElementsCollection results() {
      return $$("#ires div.g");
   }
}

実際に利用するテストクラス

@Test
public void ページオブジェクトを利用してgoole検索を実施() {
   GoogleSearchPage searchPage = open("http://google.com/", GoogleSearchPage.class);

   GoogleResultsPage resultsPage = searchPage.search("selenide");

   resultsPage.results().shouldHave(size(10));
   resultsPage.results().get(0).shouldHave(text("Selenide: Concise UI Tests in Java"));
}

ポイントとしてはページクラスに対してフィールドを定義するのではなくメソッドを定義しているところになります。

公式サイトでは以下のように記載されておりページオブジェクトにてフィールドは隠蔽され アクセスについてはメソッドを経由して実施すると書かれています。

Final OOP notice(公式サイトより引用)

Let me remind you that the initial idea behind Page Objects was to encapsulate
 (read: hide) logic of working with page elements.
Tests should not operate with page elements / locators / XPaths.
Tests should use methods of Page Object instead.
So, if you even declare fields for page page elements, please, make them private!
Let methods of Page Object be public and fields private.
Otherwise OOP is just nonsense.

google翻訳で翻訳した結果

Page Objectの背後にある最初のアイデアは、ページ要素を扱うロジックをカプセル化する(読み込み:非表示にする)ことでした。
テストはページ要素/ロケータ/ XPathで動作するべきではありません。
テストでは代わりにPageオブジェクトのメソッドを使用する必要があります。
だから、たとえページ・ページ要素のフィールドを宣言したとしても、それらを非公開にしてください!
Page Objectのメソッドを公開し、プライベートなフィールドにします。
さもなければ、OOPはちょうどナンセンスです。

といってもそこまでシビアに考える必要はないのかなと考えています。

Classic Page Object

selenideの公式サイトでも以下のような記載がありフィールドへの設定についてもサポートされています。

Classic Page Object

There is a very popular style of Page Object that implies creating a separate field for every page element.
This style has some disadvantages, but if you want, Selenide allows it:

以下のような感じでフィールドを定義することもできます。

public class GoogleSearchPage {
  @FindBy(how = How.NAME, using = "q")
  private SelenideElement searchBox;

  public GoogleResultsPage search(String query) {
    searchBox.setValue(query).pressEnter();
    return page(GoogleResultsPage.class);
  }
}

public class GoogleResultsPage {
  @FindBy(how = How.CSS, using = "#ires li.g")
  public ElementsCollection results;
}

フィールドの設定のタイミング

selenideからページオブジェクトを利用する場合には指定のメソッドを利用する必要があります。 利用シーン及び対象のメソッドは以下のようになります。

  • openメソッド

    初期表示などの場合に利用します。
    利用方法としては第一引数に対象のURLを設定し、第二引数にページクラスを設定します。
    GoogleSearchPage searchPage = open("http://google.com/", GoogleSearchPage.class);
    
  • pageメソッド

    画面表示後のページを表示時等に利用します。

    page(GoogleResultsPage.class);
    

フィールドへの要素設定(@FindBy)

フィールドへの値設定にアノテーションを利用することで容易に設定することができます。 (アノテーション自体はselenideではなく、seleniumの機能となります。)

利用できる種類は以下のようになります。

  • how + using

    howにて種類を指定し、usingにて値を設定します。

  • id

    cssのid属性を設定します。

  • name

    htmlのname属性を利用して設定します。

  • className

    cssのclass名を利用して設定します。

  • css

    cssのセレクターを利用して設定します。

  • tagName

    htmlのタグの名称を利用して設定します。

  • linkText

    aタグのテキストを利用して設定します。(完全一致)

  • partialLinkText

    aタグのテキストを利用して設定します。(部分一致)

  • xpath

    xpathを利用して設定します。