Introduction
Creating Page Objects is only the first step toward building a maintainable automation framework. To get the maximum benefit from the Page Object Model (POM), it is important to follow a set of best practices while designing your page classes.
Well-designed Page Objects make your automation framework easier to read, maintain, and extend. They also reduce code duplication, improve reusability, and make test cases more understandable.
In this tutorial, you’ll learn the most important Page Object Best Practices in Selenium with Python, along with an example, real-world scenarios, common mistakes, and recommendations followed in professional automation frameworks.
Why Follow Page Object Best Practices?
Following best practices helps you:
Write clean and maintainable automation code.
Reduce duplication across test cases.
Keep test scripts simple and readable.
Make UI changes easier to manage.
Improve framework scalability.
Build reusable automation components.
Common Page Object Best Practices
Some widely accepted Page Object best practices include:
Keep locators inside the Page Class.
Write methods that represent user actions.
Keep assertions inside test cases, not Page Classes.
Return self when method chaining is useful.
Create one Page Class for each webpage.
Use meaningful and descriptive method names.
Keep Page Classes focused only on page interactions.
Following these practices results in a cleaner and more professional automation framework.
Example
from selenium import webdriver
from selenium.webdriver.common.by import By
# Topic: 44. Page Object Model (POM) - Page Object Best Practices
# Practice site: https://www.testmuai.com/selenium-playground/radiobutton-demo
# Run: pytest -s 44_examples/test_03_page_object_best_practices.py
#
# Best practices: no assertions in pages, return self for fluent actions, keep
# locators private to the page, and name methods from the user perspective.
class RadioButtonDemoPage:
URL = "https://www.testmuai.com/selenium-playground/radiobutton-demo"
_MALE = (By.CSS_SELECTOR, "input[value='Male']")
def __init__(self, driver):
self.driver = driver
def open(self):
self.driver.get(self.URL)
return self
def select_male(self):
self.driver.find_element(*self._MALE).click()
return self
def is_male_selected(self):
return self.driver.find_element(*self._MALE).is_selected()
def test_page_object_best_practices():
driver = webdriver.Chrome()
try:
page = RadioButtonDemoPage(driver).open().select_male()
assert page.is_male_selected()
finally:
driver.quit()
Understanding the Code
Import Required Libraries
from selenium import webdriver
from selenium.webdriver.common.by import By
These modules are required to launch the browser and locate web elements.
Create the Page Class
class RadioButtonDemoPage:
A Page Class is created to represent the Radio Button Demo webpage.
The class contains everything related to this page, including its locators and user actions.
Store the Page URL
URL = "https://www.testmuai.com/selenium-playground/radiobutton-demo"
The webpage URL is stored inside the Page Class.
This avoids hardcoding URLs in multiple test cases.
Keep Locators Private
_MALE = (
By.CSS_SELECTOR,
"input[value='Male']"
)
Notice the locator name begins with an underscore (_).
This indicates that the locator is intended for internal use within the Page Class and should not be accessed directly by test cases.
Keeping locators private improves encapsulation and prevents accidental misuse.
Initialize the Driver
def __init__(self, driver):
self.driver = driver
The constructor stores the WebDriver instance so that all page methods can use it.
Open the Webpage
def open(self):
self.driver.get(self.URL)
return self
The open() method navigates to the webpage.
Returning self allows method chaining, making test code shorter and easier to read.
Example:
page.open().select_male()
Create User-Oriented Methods
def select_male(self):
Method names should describe what the user does, not how Selenium performs the action.
A name like select_male() clearly explains the action being performed.
Perform the Click Action
self.driver.find_element(
*self._MALE
).click()
The method locates the radio button and clicks it.
The Selenium code remains inside the Page Class instead of being repeated in test cases.
Return the Current Page Object
return self
Returning self enables multiple page methods to be chained together.
This keeps test scripts concise and readable.
Provide a Getter Method
def is_male_selected(self):
Instead of exposing Selenium code to the test, the Page Class provides a method that returns whether the radio button is selected.
Keep Assertions in the Test
assert page.is_male_selected()
Notice that the assertion is written inside the test function, not inside the Page Class.
This follows one of the most important Page Object principles:
Page Classes perform actions.
Test cases perform validations.
Separating these responsibilities makes the framework easier to maintain.
Close the Browser
driver.quit()
Closes the browser and ends the WebDriver session.
Practical Example
Suppose an application has a Login Page.
The Page Class can contain methods such as:
enter_username()enter_password()click_login()
The test case performs the validation:
Verify successful login.
Verify error message.
Verify page title.
This keeps responsibilities clearly separated.
Automation Testing Example
Consider an online banking application.
The TransferPage class performs actions such as:
Enter account number.
Enter transfer amount.
Click Transfer.
The test case verifies:
Transfer completed successfully.
Confirmation message appears.
Updated account balance is displayed.
Real-World Example
Professional automation frameworks follow these Page Object best practices in:
Banking applications
E-commerce websites
Healthcare systems
Insurance portals
CRM applications
ERP software
Enterprise web applications
These practices make large automation projects easier to maintain as the application grows.
Advantages of Following Page Object Best Practices
Improves code readability.
Makes maintenance easier.
Reduces duplicate Selenium code.
Encourages code reuse.
Keeps responsibilities separate.
Supports scalable automation frameworks.
Makes tests easier to understand.
Common Mistakes Beginners Make
Writing Assertions Inside Page Classes
Page Classes should perform actions only.
Assertions belong in the test cases.
Making Locators Public
Avoid exposing locators outside the Page Class.
Keep them private whenever possible.
Writing Generic Method Names
Avoid names like:
click()
Instead use descriptive names such as:
select_male()
click_login()
search_product()
These names clearly describe the user’s action.
Mixing Multiple Pages in One Class
Each webpage should have its own dedicated Page Class.
Avoid creating large classes that represent multiple pages.
Writing Selenium Code Directly in Tests
Move page interactions into Page Classes instead of repeating Selenium code in every test.
Best Practices
Keep locators private inside the Page Class.
Create one Page Class per webpage.
Write methods from the user’s perspective.
Return self when method chaining improves readability.
Keep assertions inside test cases.
Keep Page Classes responsible only for page interactions.
Use clear and meaningful method names.
Conclusion
Following Page Object Best Practices helps create automation frameworks that are clean, reusable, and easy to maintain. By keeping locators private, writing user-focused methods, separating actions from assertions, and organizing each webpage into its own Page Class, you can build professional Selenium frameworks that scale well as applications grow.
Frequently Asked Questions (FAQs)
Why should assertions not be placed inside Page Classes?
Page Classes should only perform page interactions.
Assertions belong in test cases because they verify the application’s behavior.
Why are locators kept private?
Private locators prevent direct access from test cases and improve encapsulation.
Why return self from page methods?
Returning self enables method chaining, making test scripts shorter and easier to read.
Should one Page Class represent multiple webpages?
No.
Each webpage should have its own dedicated Page Class.
Why should method names describe user actions?
Methods such as click_login() or select_male() make the automation code easier to understand and closely reflect real user behavior.
Key Takeaways
Keep locators private inside the Page Class.
Create user-friendly methods that describe actions.
Keep assertions inside test cases.
Return
selfwhen method chaining is useful.Create one Page Class for each webpage.
Follow these best practices to build clean, maintainable, and scalable Selenium automation frameworks.
