Principle:Webdriverio Webdriverio Page Object Model
Metadata
| Field | Value |
|---|---|
| Page ID | Page_Object_Model |
| Wiki | Webdriverio_Webdriverio |
| Type | Principle |
| Domains | Testing, Design_Patterns |
| Knowledge Sources | Repo (https://github.com/webdriverio/webdriverio), Doc (https://webdriver.io/docs/pageobjects), Blog (https://martinfowler.com/bliki/PageObject.html) |
| Related Implementations | Implementation: Page_Object_Class_Pattern |
Overview
A design pattern for structuring browser automation tests by encapsulating page elements and actions into reusable class objects. The Page Object Model (POM) provides a layer of abstraction between test scripts and the underlying page structure, ensuring that changes to the application's UI only need to be updated in one place rather than across every test that interacts with the affected elements.
Description
The Page Object Model (POM) separates test logic from page structure by creating a class for each page (or page component) of the application under test. Each class encapsulates element selectors as getter properties and user actions as methods. Tests interact with page object instances rather than raw selectors, improving maintainability (selector changes affect only one place), readability (tests read as user flows), and reusability (page objects shared across tests).
In the context of WebdriverIO, page objects leverage the framework's ChainablePromiseElement pattern. Element getters return the result of $('selector') without await, which creates a lazy-evaluated chainable promise that is resolved only when an action is performed on it (such as .click(), .addValue(), or .getText()). This deferred evaluation means element lookups happen at interaction time, not at definition time, which avoids stale element reference errors and allows page objects to be defined statically as singletons.
The pattern consists of three layers:
- Base Page class -- Contains shared functionality such as the
open(path)method that navigates to a URL usingbrowser.url(path). - Specific Page classes -- Extend the base class and define page-specific element getters (via
get elementName() { return $('selector') }) and action methods (e.g.,submit(),login(username, password)). - Test specs -- Import page object singletons and call their methods to perform test flows, making assertions on element state.
Usage
Use the Page Object Model when building a scalable test suite for an application with multiple pages or distinct UI sections. Create a base Page class with shared navigation logic, then extend it for each application page. Tests import page object singletons and call their methods. The pattern is especially valuable when the same page elements are used across many tests.
When to apply:
- The application under test has multiple pages or views.
- Multiple tests interact with the same page elements.
- Selectors are likely to change over time (UI redesigns, component refactors).
- The team wants tests that read like user stories rather than DOM traversals.
When to avoid:
- For trivial single-page test scripts that will not be maintained.
- When test files are one-off and not shared across a team.
Typical project structure:
project-root/
pageobjects/
page.js # Base Page class
login.page.js # Login page object
form.page.js # Form page object
specs/
login.spec.js # Login tests
form.spec.js # Form tests
wdio.conf.js # WDIO configuration
Theoretical Basis
The Page Object Model follows the Facade pattern from object-oriented design. The page object provides a simplified interface to the complex DOM structure, hiding the implementation details of element selection and interaction behind descriptive method names.
Key design principles:
- Encapsulation -- Selectors are private to the page object. Tests never reference raw CSS or XPath selectors directly.
- Lazy evaluation -- Getter properties return
$('selector')withoutawait, creating ChainablePromiseElements that resolve only when acted upon. This ensures elements are fetched fresh from the DOM at the moment of interaction. - Template Method pattern -- The base class defines the
open()method skeleton, and subclasses override it with specific URL paths viasuper.open('specific-path'). - Singleton export -- Each page module exports
new PageClass(), providing a single shared instance. This is safe because page objects are stateless; they do not cache element references. - Single Responsibility Principle -- Each page object is responsible for exactly one page or component of the application.
The pattern was originally described by Martin Fowler and later popularized by the Selenium community. WebdriverIO's implementation benefits from JavaScript's getter syntax and the framework's built-in promise chaining, making the pattern particularly concise.
Related Pages
Implementation:Webdriverio_Webdriverio_Page_Object_Class_Pattern
- implemented_by Implementation: Page_Object_Class_Pattern