1 - ページオブジェクトモデル
Note: this page has merged contents from multiple sources, including
the Selenium wiki
Overview
Within your web app’s UI, there are areas where your tests interact with.
A Page Object only models these as objects within the test code.
This reduces the amount of duplicated code and means that if the UI changes,
the fix needs only to be applied in one place.
Page Object is a Design Pattern that has become popular in test automation for
enhancing test maintenance and reducing code duplication. A page object is an
object-oriented class that serves as an interface to a page of your AUT. The
tests then use the methods of this page object class whenever they need to
interact with the UI of that page. The benefit is that if the UI changes for
the page, the tests themselves don’t need to change, only the code within the
page object needs to change. Subsequently, all changes to support that new UI
are located in one place.
Advantages
- There is a clean separation between the test code and page-specific code, such as
locators (or their use if you’re using a UI Map) and layout.
- There is a single repository for the services or operations the page offers
rather than having these services scattered throughout the tests.
In both cases, this allows any modifications required due to UI changes to all
be made in one place. Helpful information on this technique can be found on
numerous blogs as this ‘test design pattern’ is becoming widely used. We
encourage readers who wish to know more to search the internet for blogs
on this subject. Many have written on this design pattern and can provide
helpful tips beyond the scope of this user guide. To get you started,
we’ll illustrate page objects with a simple example.
Examples
First, consider an example, typical of test automation, that does not use a
page object:
There are two problems with this approach.
- There is no separation between the test method and the AUT’s locators (IDs in
this example); both are intertwined in a single method. If the AUT’s UI changes
its identifiers, layout, or how a login is input and processed, the test itself
must change.
- The ID-locators would be spread in multiple tests, in all tests that had to
use this login page.
Applying the page object techniques, this example could be rewritten like this
in the following example of a page object for a Sign-in page.
and page object for a Home page could look like this.
So now, the login test would use these two page objects as follows.
There is a lot of flexibility in how the page objects may be designed, but
there are a few basic rules for getting the desired maintainability of your
test code.
Assertions in Page Objects
Page objects themselves should never make verifications or assertions. This is
part of your test and should always be within the test’s code, never in a page
object. The page object will contain the representation of the page, and the
services the page provides via methods but no code related to what is being
tested should be within the page object.
There is one, single, verification which can, and should, be within the page
object and that is to verify that the page, and possibly critical elements on
the page, were loaded correctly. This verification should be done while
instantiating the page object. In the examples above, both the SignInPage and
HomePage constructors check that the expected page is available and ready for
requests from the test.
Page Component Objects
A page object does not necessarily need to represent all the parts of a
page itself. This was noted by Martin Fowler in the early days, while first coining the term “panel objects”.
The same principles used for page objects can be used to
create “Page Component Objects”, as it was later called, that represent discrete chunks of the
page and can be included in page objects. These component objects can
provide references to the elements inside those discrete chunks, and
methods to leverage the functionality or behavior provided by them.
For example, a Products page has multiple products.
Each product is a component of the Products page.
The Products page HAS-A list of products. This object relationship is called Composition. In simpler terms, something is composed of another thing.
The Product component object is used inside the Products page object.
So now, the products test would use the page object and the page component object as follows.
The page and component are represented by their own objects. Both objects only have methods for the services they offer, which matches the real-world application in object-oriented programming.
You can even
nest component objects inside other component objects for more complex
pages. If a page in the AUT has multiple components, or common
components used throughout the site (e.g. a navigation bar), then it
may improve maintainability and reduce code duplication.
Other Design Patterns Used in Testing
There are other design patterns that also may be used in testing. Discussing all of these is
beyond the scope of this user guide. Here, we merely want to introduce the
concepts to make the reader aware of some of the things that can be done. As
was mentioned earlier, many have blogged on this topic and we encourage the
reader to search for blogs on these topics.
Implementation Notes
PageObjects can be thought of as facing in two directions simultaneously. Facing toward the developer of a test, they represent the services offered by a particular page. Facing away from the developer, they should be the only thing that has a deep knowledge of the structure of the HTML of a page (or part of a page) It’s simplest to think of the methods on a Page Object as offering the “services” that a page offers rather than exposing the details and mechanics of the page. As an example, think of the inbox of any web-based email system. Amongst the services it offers are the ability to compose a new email, choose to read a single email, and list the subject lines of the emails in the inbox. How these are implemented shouldn’t matter to the test.
Because we’re encouraging the developer of a test to try and think about the services they’re interacting with rather than the implementation, PageObjects should seldom expose the underlying WebDriver instance. To facilitate this, methods on the PageObject should return other PageObjects. This means we can effectively model the user’s journey through our application. It also means that should the way that pages relate to one another change (like when the login page asks the user to change their password the first time they log into a service when it previously didn’t do that), simply changing the appropriate method’s signature will cause the tests to fail to compile. Put another way; we can tell which tests would fail without needing to run them when we change the relationship between pages and reflect this in the PageObjects.
One consequence of this approach is that it may be necessary to model (for example) both a successful and unsuccessful login; or a click could have a different result depending on the app’s state. When this happens, it is common to have multiple methods on the PageObject:
The code presented above shows an important point: the tests, not the PageObjects, should be responsible for making assertions about the state of a page. For example:
could be re-written as:
Of course, as with every guideline, there are exceptions, and one that is commonly seen with PageObjects is to check that the WebDriver is on the correct page when we instantiate the PageObject. This is done in the example below.
Finally, a PageObject need not represent an entire page. It may represent a section that appears frequently within a site or page, such as site navigation. The essential principle is that there is only one place in your test suite with knowledge of the structure of the HTML of a particular (part of a) page.
Summary
- The public methods represent the services that the page offers
- Try not to expose the internals of the page
- Generally don’t make assertions
- Methods return other PageObjects
- Need not represent an entire page
- Different results for the same action are modelled as different methods
Example
2 - ドメイン固有言語(DSL)
ドメイン固有言語(DSL)は、問題を解決するための表現手段をユーザーに提供するシステムです。
それによって、ユーザーは、プログラマーの言葉でなく、自分の言葉でシステムとやりとりすることができます。
通常、ユーザーはサイトの外観を気にしません。
装飾、アニメーション、グラフィックスは気にしません。
彼らはあなたのシステムを使用して、新しい従業員を最小限の難しさでプロセスに押し込みたいと考えています。
彼らはアラスカへの旅行を予約したい。
ユニコーンを設定して割引価格で購入したいのです。
テスターとしてのあなたの仕事は、この考え方を"とらえる"ことにできるだけ近づくことです。
それを念頭に置いて、テストスクリプト(ユーザーの唯一のプレリリースの代理人)がユーザーを"代弁し"、表現するように、作業中のアプリケーションの"モデリング"に取り掛かります。
Seleniumでは、DSLは通常、APIをシンプルで読みやすいように記述したメソッドで表されます。
開発者と利害関係者(ユーザー、製品所有者、ビジネスインテリジェンススペシャリストなど)との伝達が可能になります。
利点
- Readable: ビジネス関係者はそれを理解できます。
- Writable: 書きやすく、不要な重複を避けます。
- Extensible: 機能は(合理的に)契約と既存の機能を壊すことなく追加できます。
- Maintainable: 実装の詳細をテストケースから除外することにより、AUT* の変更に対して十分に隔離されます。
Java
Javaの妥当なDSLメソッドの例を次に示します。
簡潔にするために、driver
オブジェクトが事前に定義されており、メソッドで使用可能であることを前提としています。
このメソッドは、テストコードから入力フィールド、ボタン、クリック、さらにはページの概念を完全に抽象化します。
このアプローチを使用すると、テスターはこのメソッドを呼び出すだけで済みます。
これにより、メンテナンスの利点が得られます。
ログインフィールドが変更された場合、テストではなく、このメソッドを変更するだけで済みます。
繰り返しになります。
主な目標の1つは、 テストが UIの問題ではなく、手元の問題 に対処できるAPIを作成することです。
UIはユーザーにとって二次的な関心事です。ユーザーはUIを気にせず、ただ仕事をやりたいだけです。
テストスクリプトは、ユーザーがやりたいことと知りたいことの長々としたリストのように読む必要があります。
テストでは、UIがどのようにそれを実行するように要求するかについて、気にするべきではありません。
*AUT: Application under test(テスト対象アプリケーション)
6 - ロケータをうまく扱うTips
どのロケータを指定すべきか、コード内でロケータをどう管理すると良いか。
サポートしているロケータについては 要素を探すを参照してください。
一般に、HTMLのid属性が利用可能でユニークかつ一貫している場合、ページで要素を探す方法として適しています。
idは動作がとても速い傾向があり、複雑なDOMトラバースに伴う処理を省略できます。
ユニークなidが使えない場合、きれいに書かれたCSSセレクタが要素を探す方法として適しています。
XPathはCSSセレクタと同様に動作しますが、シンタックスは複雑で大抵の場合デバッグが困難です。
XPathはとても柔軟ですが、ブラウザベンダは性能テストを通常行っておらず、非常に動作が遅い傾向があります。
link textセレクタとpartial linkText セレクタはa要素でしか動作しないという欠点があります。
加えて、これらはWebDriverの内部でquerySelectorAllの呼び出しに置き換えられます。
タグ名によるロケータは危険な方法になり得ます。
大抵の場合ページ上には同じタグ名の要素が複数あります。タグ名は要素のコレクションを返す findElements(By) メソッドを使う時にもっとも役に立ちます。
ロケータは可能な限り簡潔に、読みやすい状態を保つことを推奨します。
WebDriverでDOM構造のトラバースを行うのは重い処理となります。
検索の範囲を狭めた方がより良い結果を得られます。
8 - テストの独立性
各テストを独自のユニットとして記述します。
他のテストに依存しない方法でテストを記述してください。
公開後にモジュールとしてWebサイトに表示されるカスタムコンテンツを作成できるコンテンツ管理システム(CMS)があり、CMSとアプリケーション間の同期に時間がかかる場合があるとします。
モジュールをテストする間違った方法は、1つのテストでコンテンツが作成および公開され、別のテストでモジュールをチェックすることです。
コンテンツは公開後、他のテストですぐに利用できない可能性があるため、この方法はふさわしくありません。
代わりに、影響を受けるテスト内でオン/オフできるスタブコンテンツを作成し、それをモジュールの検証に使用できます。
ただし、コンテンツの作成については、別のテストを行うことができます。