The page object pattern is an extremely important pattern that’s well described over at Selenium. If you don’t know about the pattern and haven’t read their article, go read it now.
Everything said in the article also applies to dialogs, so don’t be misled by the word page. They could just as well have written about dialog objects.
The article also suggested a wonderful practice that’s new to me: Having a method in a page object return another page object. I’m now doing this whenever a method lands the application on a different page. Example: Completing the sign-in on the sign-in page lands the app on the home page, so method
SignInPage.SignIn returns a
So what can I add? Well, DRYness! (Don’t Repeat Yourself!)
[This post originally emphasized inheritance. David Green commented that composition is more flexible. I agree, so I’ve revised the post.]
I’m going to talk about composition and inheritance here because the language I’m using is C#. The corresponding strategy in a different language might be different: Ruby mixins, Python modules, etc.
Let’s get to it. The web application I’m testing, like many others, has some elements that appear on many but not all of its pages. For the sake of DRYness, it’s useful to handle these elements with composition and inheritance (or other code-sharing strategy, as above).
The first class for page encapsulation is the base class
_BasePage. I’ve given the class name an underscore to emphasize that it’s not an ordinary page class — it is not itself the encapsulator of a single web page.
Every page class derives from
_BasePage? Only those things needed for every page in the application:
- Support for the
Locatorobjects that will tell CUIT how to find controls.
I’ll write about classes
Locator in other posts.
In the application:
- The menu bar appears on every page except a popover and the sign-in page.
- The footer appears on every page except a popover. In this particular app, a popover is sufficiently page-like to be treated as a separate page.
The other elements that are shared among pages are handled via composition, because different pages may have different mixtures of those elements.
Each of the compositors is a class whose constructor accepts a page object. The compositor defines locators and adds those locators to the given page.
Almost all pages have the menu bar, which has the basic navigation for the application. The page objects for such a page declares an instance variable
_Menubar menubar and assigns it a new instance of
_Menubar, whose constructor adds the appropriate locators to the page object.
Like the menu bar, most pages have footer boilerplate. The page object for such a page declares an instance variable
_Footer and assigns it a new instance of
_Footer, whose constructor adds the appropriate locators to the page object.
A few pages in the application have tab-like behaviors. On such a page, there’s an upper area with a title and two or more links (tabs). Below there’s content that can be changed by clicking one of the links.
Let’s say that there are four tabs, for the four seasons.
Then a class
_Seasons encapsulates the elements in the upper part of the page.
Each of the tabs is encapsulated by a page object (derived from
_BasePage). Each page object declares an instance variable
_Seasons seasons and assigns it a new instance of
_Seasons, whose constructor adds the appropriate locators to the page object.
So each season tab has its own locators, plus those in
_Seasons, plus any it chooses to get from
Now that’s DRY!