Modern QA2026Classes for Page Objects — tiles
Log inJoin
28 / 80 · 12 Programming for QA · OOP for Testing← prev⊞ allnext →☰ Read as one page

4.2Classes for Page Objects

A class encapsulates data (locators) and behavior (actions) for a single page or component. This separation keeps test files clean and localizes changes to one place.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class LoginPage:
    URL = "/login"
    EMAIL = (By.CSS_SELECTOR, "input[name='email']")
    PASSWORD = (By.CSS_SELECTOR, "input[name='password']")
    SUBMIT = (By.CSS_SELECTOR, "button[type='submit']")
    ERROR_MESSAGE = (By.CSS_SELECTOR, ".error-message")

    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    def open(self):
        self.driver.get(f"{BASE_URL}{self.URL}")
        return self

    def login(self, email: str, password: str) -> "DashboardPage":
        self.driver.find_element(*self.EMAIL).send_keys(email)
        self.driver.find_element(*self.PASSWORD).send_keys(password)
        self.driver.find_element(*self.SUBMIT).click()
        return DashboardPage(self.driver)

    def get_error_message(self) -> str:
        element = self.wait.until(EC.visibility_of_element_located(self.ERROR_MESSAGE))
        return element.text

class DashboardPage:
    WELCOME_HEADER = (By.CSS_SELECTOR, "h1.welcome")
    LOGOUT_BUTTON = (By.CSS_SELECTOR, "[data-testid='logout']")

    def __init__(self, driver):
        self.driver = driver

    def get_welcome_text(self) -> str:
        return self.driver.find_element(*self.WELCOME_HEADER).text

    def logout(self) -> "LoginPage":
        self.driver.find_element(*self.LOGOUT_BUTTON).click()
        return LoginPage(self.driver)

Using Page Objects in Tests

def test_login_success(driver):
    dashboard = LoginPage(driver).open().login("user@test.com", "pass123")
    assert dashboard.get_welcome_text() == "Welcome"

def test_login_invalid_password(driver):
    login_page = LoginPage(driver).open()
    login_page.login("user@test.com", "wrongpass")
    assert login_page.get_error_message() == "Invalid email or password"

The test reads like a user story. Locators and interactions are hidden inside the page object. If the CSS selector for the email field changes, you update one line in LoginPage, not every test.