30 / 80 · 12 Programming for QA · OOP for Testing← prev⊞ allnext →☰ Read as one page
4.4Composition: Reusable Components
Composition means building pages from independent, reusable components instead of inheriting behavior from parent classes.
class NavigationComponent:
MENU_BUTTON = (By.CSS_SELECTOR, "[data-testid='menu']")
SEARCH_INPUT = (By.CSS_SELECTOR, "[data-testid='search']")
def __init__(self, driver):
self.driver = driver
def open_menu(self):
self.driver.find_element(*self.MENU_BUTTON).click()
def search(self, query: str):
self.driver.find_element(*self.SEARCH_INPUT).send_keys(query)
class NotificationComponent:
BELL_ICON = (By.CSS_SELECTOR, "[data-testid='notifications']")
COUNT_BADGE = (By.CSS_SELECTOR, ".notification-count")
def __init__(self, driver):
self.driver = driver
def get_count(self) -> int:
return int(self.driver.find_element(*self.COUNT_BADGE).text)
class DashboardPage:
def __init__(self, driver):
self.driver = driver
self.nav = NavigationComponent(driver) # composition
self.notifications = NotificationComponent(driver) # composition
# Dashboard-specific methods
def get_welcome_text(self) -> str:
return self.driver.find_element(By.CSS_SELECTOR, "h1.welcome").text
Using Composed Page Objects
def test_dashboard_navigation(driver):
dashboard = DashboardPage(driver)
dashboard.nav.search("settings") # uses nav component
assert dashboard.notifications.get_count() >= 0 # uses notification component
Inheritance vs Composition
| Approach | When to Use | Example |
|---|---|---|
| Inheritance | Pages share common behavior (base page utilities) | class AdminPage(BasePage) |
| Composition | Pages contain reusable UI components | self.nav = NavigationComponent(driver) |
Prefer composition. Components that you plug into pages are easier to maintain, test independently, and reuse across different pages. Reserve inheritance for a thin BasePage with shared utilities.