Simple modern page models for Selenium
- What is a page model?
- Basics: Site
- Basics: Page
- Basics: Finder
- Design principles
- Page components
- Page actions
- Handling multiple windows
- Failure exceptions
What is a page model?
A page model is a class that models how a person interacts with a specific page in the UI. The public methods of a page model represent the tasks that users perform on the page -- what they see and what they say to get things done and to understand the results. The private methods of a page model implement the specific interactions with page content needed to perform these tasks.
The page model pattern helps to keep UI tests more resilient to changes in the UI. Because tests use only the public interface to pages, they are isolated from details of page construction. Page layout and content can be revised and reorganized with requiring any changes to test scenarios or test assertions.
Site class to model the overall context for testing a specific Web app. This
context includes not only information about the app itself (for example, the base
URI for app pages) but also information about the testing context (for example the
WebDriver instance used to access the UI).
Page objects must be created in the context of a specific
Site. That's why
Page is a generic type parameterized by the type of its
Site can be configured to reflect the latencies that occur when interacting with the app. For example, the maximum app wait
time can be configured to reflect the maximum timeout to wait for this particular
app to update its page elements. In addition, the driver latency factor can be
configured to reflect additional latencies incurred when using a particular remote
Site class is typically used as a base class for classes that model additional application-specific
ResourceSite implements a special-purpose
Site that provides Web
pages defined by resource files.
Site class defines two basic behaviors for tests.
|Method||What it does|
Page class is the foundation for all page models. This abstract base class offers many different forms
for constructors that model the various ways that a new page can appear in the UI.
- By direct navigation to specific URL
- By a request from a prior page (called the "parent" page)
- By a request to open a new browser window
Page is always associated with a
Site instance and inherits many of its properties. For example,
Page.getDriver() returns the
WebDriver used by the
Site for this page. Similarly, by default,
Page.getMaxAppWait() returns the maximum app wait time defined for
Site. (But note that you can replace this default with a different maximum wait time for any specific
Page class defines the basic methods used by a page model to:
- search for page elements, using a
- handle pages in multiple windows, and
- find and perform the page actions represented by page elements.
Finder object defines how to search for a specific page element. Fundamentally,
a page element must be identified by matching it to some
By condition. (For example,
elements using a CSS selector expression.) But a
Finder contains many other optional parameters that may be needed to reliably guide the
- The top-level element within which the search is performed
- The maximum time to wait before giving up the search
- How often to check for the presence of matching element(s)
- Any additional condition that matching element(s) must satisfy
Finder class defines the following basic search methods.
|Method||What it does|
||Returns a specific element that is assumed to exist|
||Returns a list of all matching elements|
||Returns successfully when no matching elements can be found|
Finder also provides a convenient "fluent" interface for defining the search context, using sensible defaults for unspecified parameters. For example:
List<WebElement> hasDataDefined = startingAt( someDivElement) .waitingFor( 30, SECONDS) .checkingEvery( 200, MILLISECONDS) .when( PageUtils.hasAttribute( "data-defined")) .whenStableFor( 1, SECONDS) .findElements( By.cssSelector( ".someClass"));
Page models seldom create
Finder instances directly. Instead, it's simpler to use the
Page methods that provide
the same interfaces for defining and executing an element search.
Less is more. Crescent is a very minimal extension of the basic
WebDriverAPI. It makes no rules about the internal structure of a page model. It does not try to model individual interaction techniques, like menus or various
<input>types. (Although the
PageUtilsclass offers many useful helper methods for that sort of thing.) Less framework means more freedom for you to choose the right way to model your UI.
BYO WebDriver. Crescent has no support for creating a
WebDriverand connecting it to a browser. Many are the ways in which this can be done, depending on your test environment, your preferred tools for configuration, and so on. You are free to choose the best way to handle this part of your test setup.
When a page is (re)visited, create a new
Page. Because this is a good practice for any
Pageclass assumes that you will create a new
Pageinstance when the corresponding page becomes available for interaction in the UI. Therefore, every page model constructor leads to the basic
Pageconstructor, which calls
initPage(). By default, this calls
visit(), which performs the following steps.
Synchronizes the state of the
Pagewith the state of the UI. Depending on the constructor arguments, this can entail actively navigating to a specific URL, passively querying the current location of the browser, or switching the
WebDriverto a specified browser window.
visited(). By default, this method does nothing. But you can override it to perform any page-specific actions needed after arriving at a new instance of this page.
Error states are exceptions. Users make mistakes. So good UIs help them recover by showing an "error state" -- for example, by displaying an error message. And good tests verify that these error states appear as expected. But how? Tests use the public page model interface, which models the user's tasks. But checking for errors is not the user's job! That job belongs to the page model implementation. When an error state is detected (expected or not!), the page model should throw an exception. Crescent provides several basic exception types that are handy for reporting the details of an error state.
For many Web apps, all individual pages share many common graphic design forms or interaction techniques. You can use the
Component class to model common UI elements that are shared by multiple page
Component always belongs to a specific
Page instance. That's why
Component is a generic type parameterized by the type of
Some page elements implement actions that produce a certain result in the UI. For example, clicking a button can cause a different page to
appear. It's not unusual for a UI to present multiple elements that all implement the same action. The
PageAction class offers a general
way to model this kind of interaction.
PageAction is a generic type that is parameterized by the type of the source page and the type of the result produced by the action.
There are two basic
PageAction types, each of which are abstract base classes you can use to model app-specific actions.
ElementAction: An action triggered by an interaction with a single element
NewWindowAction: An action that produces content in a new browser window
Page class defines the following methods for handling page actions.
|Method||What it does|
||Returns a specific type of
||Given an optional
Handling multiple windows
Although it's common for Web apps to display content in multiple browser windows, it's tricky to handle that using the basic
API. But the
WindowProducer class makes things a bit simpler.
WindowProducer is an abstract base class for an action that opens a new window. The basic
WindowProducer.open() handles the bookkeeping
needed to discover and return the
WindowHandle for the expected new window. Together with a specific
WindowProducer, you can use the
NewWindowAction class to model not only the interaction that produces the new window, but also the type of content now
available for interaction.
Given a specific
WindowHandle argument for a new
Page, the basic
visit() method will automatically switch the focus of the
WebDriver to the specified window. Afterwards, your test must be responsible for keeping track of which
Page instances are modeling
content for different windows. But, for any
Page instance, when you call
close(), it will automatically switch the
back to the window for the
Because error states should cause exceptions, Crescent provides several basic exception types that are handy for reporting the details of an error state.
||Base class for all error states reported by a
||Reports a failure when searching for a specific page element|
||Reports a general error state in a
||Reports a failure in a system request made from a
||Reports a failure to create a new browser window|