ページオブジェクト
2013年9月10日
Webページに対してテストを書く場合、リンクをクリックしたり、何が表示されているかを判断したりするために、そのWebページ内の要素を参照する必要があります。しかし、HTML要素を直接操作するテストを書くと、UIの変更に対してテストが脆くなってしまいます。ページオブジェクトは、HTMLページ、またはフラグメントをアプリケーション固有のAPIでラップし、HTMLを詳しく調べなくてもページ要素を操作できるようにします。

ページオブジェクトの基本的な経験則は、ソフトウェアクライアントが人間ができることなら何でもでき、何でも見ることができるようにすることです。また、プログラミングが容易で、ウィンドウの基礎となるウィジェットを隠すインターフェースを提供する必要があります。そのため、テキストフィールドにアクセスするには、文字列を受け取り、返すアクセサメソッドを用意し、チェックボックスにはブール値を使用し、ボタンはアクション指向のメソッド名で表現する必要があります。ページオブジェクトは、GUIコントロール自体でデータを見つけて操作するために必要なメカニズムをカプセル化する必要があります。良い経験則は、具体的なコントロールを変更することを想像することです。その場合、ページオブジェクトのインターフェースは変更されるべきではありません。
「ページ」オブジェクトという用語にもかかわらず、これらのオブジェクトは通常、各ページに対して構築されるべきではなく、むしろページ上の重要な要素に対して構築されるべきです[1]。そのため、複数のアルバムを表示するページには、複数のアルバムページオブジェクトを含むアルバムリストページオブジェクトがあります。おそらく、ヘッダーページオブジェクトとフッターページオブジェクトもあるでしょう。とはいえ、複雑なUIの階層の一部は、UIを構造化するためだけに存在します。このような複合構造は、ページオブジェクトによって公開されるべきではありません。経験則としては、アプリケーションのユーザーにとって意味のあるページの構造をモデル化することです。
同様に、別のページに移動する場合、最初のページオブジェクトは新しいページの別のページオブジェクトを返す必要があります[2]。一般的に、ページオブジェクトの操作は、基本型(文字列、日付)または他のページオブジェクトを返す必要があります。
ページオブジェクトにアサーションを含めるべきか、それともテストスクリプトがアサーションを行うためのデータを提供するだけにするべきかについては、意見が分かれています。ページオブジェクトにアサーションを含めることを支持する人は、テストスクリプトでのアサーションの重複を避け、より良いエラーメッセージを提供しやすく、より尋ねるのではなく、伝えるスタイルのAPIをサポートするのに役立つと言います。アサーションを含まないページオブジェクトを支持する人は、アサーションを含めると、ページデータへのアクセスを提供する責任とアサーションロジックが混ざり合い、ページオブジェクトが肥大化してしまうと言います。
私は、ページオブジェクトにアサーションを含めないことを支持します。一般的なアサーションのためのアサーションライブラリを提供することで、重複を避けることができると考えています。これは、優れた診断を提供しやすくするのにも役立ちます。[3]
ページオブジェクトは一般的にテストに使用されますが、アサーション自体を行うべきではありません。その役割は、基礎となるページの状態へのアクセスを提供することです。アサーションロジックを実行するのは、テストクライアントの責任です。
このパターンをHTMLの観点から説明しましたが、同じパターンは他のUIテクノロジーにも同様に適用されます。このパターンは、Java Swing UIの詳細を隠すために効果的に使用されているのを見てきました。そして、他のほとんどすべてのUIフレームワークでも広く使用されていることは間違いありません。
並行性の問題は、ページオブジェクトがカプセル化できるもう1つのトピックです。これには、ユーザーに非同期として見えない非同期操作の非同期性を隠すことが含まれる場合があります。また、UIスレッドとワーカースレッドの間で動作の割り当てを考慮する必要があるUIフレームワークのスレッド化の問題をカプセル化することも含まれる場合があります。
ページオブジェクトはテストで最も一般的に使用されますが、アプリケーションの上にスクリプトインターフェースを提供するためにも使用できます。通常は、UIの下にスクリプトインターフェースを配置するのが最善です。これは通常、複雑さが少なく、高速です。ただし、UIに過剰な動作を配置したアプリケーションでは、ページオブジェクトを使用することで、悪い状況を最大限に活用できる場合があります。(しかし、可能であれば、そのロジックを移動するようにしてください。スクリプトとUIの長期的な健全性の両方にとって、より良い結果が得られます。)
Cucumberや内部DSLなど、何らかの形式のドメイン特化言語を使用してテストを書くのが一般的です。これを行う場合は、DSLステートメントをページオブジェクトの呼び出しに変換するパーサーを用意し、ページオブジェクトの上にテストDSLを階層化するのが最善です。
テストメソッドにWebDriver APIがある場合、あなたは間違ったことをしています。 -- Simon Stewart。
UI要素からロジックを移動することを目的としたパターン(プレゼンテーションモデル、スーパーバイジングコントローラー、パッシブビューなど)は、UIを介したテストの有用性を低下させ、ページオブジェクトの必要性を軽減します。
ページオブジェクトは、カプセル化の典型的な例です。UI構造とウィジェットの詳細を他のコンポーネント(テスト)から隠します。開発中にこのような状況を探すのは良い設計原則です。「ソフトウェアの他の部分からいくつかの詳細をどのように隠すことができるか?」と自問自答してください。他のカプセル化と同様に、これには2つの利点があります。UIを操作するロジックを1か所に限定することで、システムの他のコンポーネントに影響を与えることなく、そこで変更できることをすでに強調しました。結果として得られる利点は、クライアント(テスト)コードが理解しやすくなることです。なぜなら、そこでのロジックはテストの意図に関するものであり、UIの詳細によって乱雑にならないからです。
参考文献
私は最初にこのパターンをウィンドウドライバーという名前で説明しました。しかし、それ以来、「ページオブジェクト」という用語はSelenium Webテストフレームワークによって普及し、一般的に使用される名前になりました。
Seleniumのwikiは、ページオブジェクトの使用を強く推奨し、使用方法に関するアドバイスを提供しています。また、アサーションを含まないページオブジェクトも支持しています。
あるチームは、ソフトウェアのアップグレード後にSeleniumテストスイートの2つのバージョンの更新時間を測定しました。ページオブジェクトを使用したバージョンは、最初のテストケースでは少し時間がかかりましたが、残りのテストケースでははるかに高速であることがわかりました。詳細については、Leotta et al, "Improving test suites maintainability with the page object pattern", ICSTW 2013を参照してください。
謝辞
ペリン・ファウラー、ピート・ホジソン、サイモン・スチュワートは、この記事の草稿について特に役立つコメントを提供してくれました。いつものことですが、提案と修正については、Thoughtworksの社内ソフトウェア開発リストのさまざまなメンバーに多くのことを負っています。注記
1: ここでは、「ページオブジェクト」という名前は誤解を招くという議論があります。なぜなら、ページごとに1つのページオブジェクトだけを持つべきだと考えさせられるからです。「パネルオブジェクト」のような名前の方が良いでしょう。しかし、「ページオブジェクト」という用語が受け入れられています。ネーミングが2つの難しいことの1つである理由のもう1つの例です。
2: ナビゲーションなどに応答して他のページオブジェクトを作成する責任をページオブジェクトに負わせることは、一般的なアドバイスです。ただし、一部の実務家は、ページオブジェクトが何らかの汎用ブラウザコンテキストを返すことを好み、テストはテストの流れ(特に条件付きフロー)に基づいてそのコンテキストの上に構築するページオブジェクトを制御します。彼らの好みは、テストスクリプトが次にどのページが期待されるかを知っており、この知識をページオブジェクト自体に複製する必要がないという事実に基づいています。静的に型付けされた言語を使用する場合、通常、型シグネチャにページナビゲーションが表示されるため、彼らの好みは高まります。
3: アサーションの1つの形式は、私のような、一般的にアサーションなしのスタイルを好む人にとっても問題ありません。これらのアサーションは、テストがプローブしている特定のことではなく、この時点でのページまたはアプリケーションの不変条件をチェックするものです。