PageObjectパターンではメソッドの返り値をどうするのが良いのか

Selenium WebDriverでWebのテストを自動化し始めてから悩んだ問題が、

結局PageObjectパターンではメソッドの返り値をどうすればいいの?

ということ。

Web上のサンプルコードや、公式のWiki、日本語で読める書籍などを見ていると、少しずつ言ってることが違うんですね実は。

同じように迷ったどこかのだれかも、StackOverflowに質問を書いていました。

java – Should Page Objects Return Page Objects? – Stack Overflow

answerとして

It’s a matter of style.

とか書かれているのですが、いやそういうのが知りたいんじゃないんだよなと・・・。

いくつかの本やWebサイトに書いてある内容をまとめて、少し考えてみたいと思います。

以下、コードは全てJavaで、かつSelenideやfluentleniumなどのフレームワークは使用していません。

PageObjectパターンの作法の違い

公式Wikiの場合

PageObjects · SeleniumHQ/selenium Wiki

SeleniumWikiには、以下のように書いてあります。和訳は私です。

サマリー

  • publicメソッドはそのページが提供するサービスを表す
  • ページ内部を隠蔽する
  • アサーションを書かない
  • メソッドは他のPageObjectを返す
  • ページをまるっとPageObjectで表現する必要はない
  • 同じアクションが異なる結果を返す場合、異なるメソッドとして実装する

で、ココで気にしているのが

メソッドは他のPageObjectを返す

というところです。

Wiki内に例が載っているのですが、たとえばLoginPageクラスで、ログインフォームにユーザ名を入力するメソッドは以下のように書いてあります。

public LoginPage typeUsername(String username) {
    // This is the only place that "knows" how to enter a username
    driver.findElement(usernameLocator).sendKeys(username);

    // Return the current page object as this action doesn't navigate to a page represented by another PageObject
    return this;    
}

私がSeleniumでPageObjectパターンを使い始めたときにはこの公式Wikiを参考にしたので、こうした「具体的な文字列などを返さない場合にはPageObjectクラスを返す」やり方で書いていましたが、これって”メソッドは他のPageObjectを返す”と矛盾している気もします。ついでに”publicメソッドはそのページが提供するサービスを表す”にも反している気が・・・。

ページ遷移を伴うメソッドでは、遷移先のPageObjectを返すのはわかります。でもこの例だと、遷移しないときにreturn this;しているので、「他のPageObjectを返」してはいませんよね。

なんか納得いかない。

もちろん初めてのときは「そういうもんなのか」で書いちゃってましたが・・・。

このやり方のメリットとしては、java – Should Page Objects Return Page Objects? – Stack Overflowの回答にもあるように

NextPage nextPage = somePage.fillInTextField1("value1").fillInTextField2("value2").submit();

みたいな書き方が出来ること・・・くらいかなと。実際メリットと言えるかどうかは微妙なところで、画面をはみ出すような書きっぷりはたいていのプログラマから嫌われますし。

Selenium実践入門の場合

Selenium実践入門 ―― 自動化による継続的なブラウザテスト (WEB+DB PRESS plus)の場合、

  • メソッドがページ遷移を伴わず、文字列等を返さない場合はvoid
  • メソッドがページ遷移を伴う場合は遷移先のPageObjectを返す

というルールで書かれています。

これが一番わかり易い気がしています。

ToolsQAの場合

Seleniumに関する情報がまとまっていて個人的に好きなサイト”ToolsQA”にも、PageObjectModelに関する解説があります。

Set up Page Object Model (POM) in Selenium Automation Framework

ここでは、PageObjectModelは、画面要素をストアするクラスとしても役割しか持っていないようです。つまり、画面要素に対するclicksendkeysといった操作は呼び出し元のテストクラス側で行うという・・・。

public static WebElement lnk_MyAccount(WebDriver driver){ 
  element = driver.findElement(By.id("account")); 
  return element; 
}

public static WebElement lnk_LogOut(WebDriver driver){ 
  element = driver.findElement(By.id("account_logout")); 
  return element;
}

もしかしたらPageFactoryの説明がまだだから、という理由があるのかもしれないですが、正直反対です。PageObjectパターンの意味ない気がします。

思想としては、UFT(QTP)のオブジェクトリポジトリをSeleniumでも、ということのようです。UFT(QTP)でも結局オブジェクトリポジトリの要素を使った関数ライブラリとか作るんだからもういいじゃんと・・・。

実践SeleniumWebDriverの場合

実践 Selenium WebDriverも、Selenium実践入門と一緒です。

結局Selenium実践入門と、実践SeleniumWebDriverの2冊が、公式のWikiに書いてあるサマリーを(公式のサンプルコードよりも)忠実に守っていて、かつ読みやすいメンテしやすいを実現しているように思います。

まとめ:公式の(サンプルではなく)サマリーに従って書こう

個人的な結論はコレです。
公式のサンプルコードの通りに実装して「なんだかイマイチ違うな・・・」と感じた身としては、手に取りやすいSelenium実践入門を買って、6章のページオブジェクトパターンを参考に書くのが一番だと思います。

実際メソッドを横につなげていって書いても可読性損なうだけなので・・・。

参考

ABOUTこの記事をかいた人

都内でテストエンジニア&ブロガーをやっている@yoshikiitoです。 ソフトウェアエンジニアの学習方法や成長するための考え方、会社に依存せず自分の力で生きていけるエンジニアになる方法などについて興味があります。 こういった方法や考え方、自分が試したことなどをブログを通じて発信します。 仕事は主にソフトウェアテストやテスト自動化。 趣味は浦和レッズと読書と技術書を買って積むこと。 技術評論社から本を出すのが夢