Page factory pattern in Selenium Python - Build an Automation Script
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.page_factory import PageFactory import unittest class LoginPage: def __init__(self, driver): self.driver = driver self.wait = WebDriverWait(driver, 10) self.username = (By.ID, 'username') self.password = (By.ID, 'password') self.login_button = (By.ID, 'loginBtn') def enter_username(self, username): self.wait.until(EC.visibility_of_element_located(self.username)).send_keys(username) def enter_password(self, password): self.wait.until(EC.visibility_of_element_located(self.password)).send_keys(password) def click_login(self): self.wait.until(EC.element_to_be_clickable(self.login_button)).click() class DashboardPage: def __init__(self, driver): self.driver = driver self.wait = WebDriverWait(driver, 10) self.welcome_message = (By.ID, 'welcomeMsg') def get_welcome_text(self): element = self.wait.until(EC.visibility_of_element_located(self.welcome_message)) return element.text class TestLogin(unittest.TestCase): def setUp(self): options = Options() options.add_argument('--headless') self.driver = webdriver.Chrome(options=options) self.driver.get('https://example.com/login') def test_login_success(self): login_page = LoginPage(self.driver) login_page.enter_username('testuser') login_page.enter_password('Test@1234') login_page.click_login() # Verify URL WebDriverWait(self.driver, 10).until(EC.url_to_be('https://example.com/dashboard')) self.assertEqual(self.driver.current_url, 'https://example.com/dashboard') dashboard_page = DashboardPage(self.driver) welcome_text = dashboard_page.get_welcome_text() self.assertEqual(welcome_text, 'Welcome, testuser!') def tearDown(self): self.driver.quit() if __name__ == '__main__': unittest.main()
The code uses Selenium WebDriver with Python's unittest framework.
We define two page classes: LoginPage and DashboardPage. Each class holds locators and methods to interact with page elements.
In LoginPage, methods enter username, password, and click login button using explicit waits to ensure elements are ready.
In DashboardPage, we wait for the welcome message to appear and get its text.
The test class TestLogin opens the browser in headless mode, navigates to login page, performs login steps, then verifies the URL and welcome message text.
Explicit waits avoid timing issues. Locators use IDs for reliability. The Page Factory pattern is followed by separating page logic from test logic, making code clean and maintainable.
Now add data-driven testing with 3 different username and password combinations to verify login success or failure.
Practice
Page Factory pattern in Selenium with Python?Solution
Step 1: Understand the role of Page Factory
Page Factory helps organize web elements by grouping them in a class, making tests easier to read and maintain.Step 2: Compare with other options
Options B, C, and D describe different Selenium features unrelated to Page Factory's main purpose.Final Answer:
To group web elements in a class for easier access and maintenance -> Option CQuick Check:
Page Factory = Group elements for easy access [OK]
- Confusing Page Factory with test execution speed
- Thinking it generates reports
- Assuming it handles browser alerts
Solution
Step 1: Identify Page Factory syntax in Python
Page Factory uses class attributes with locator tuples like(By.ID, 'username')to declare elements before initializing them.Step 2: Check each option
username = (By.ID, 'username')shows the correct locator tuple usage, which is standard in Page Factory.Final Answer:
username = (By.ID, 'username') -> Option BQuick Check:
Page Factory uses locator tuples (By.xx, 'value') [OK]
- Using direct driver.find_element calls instead of Page Factory declarations
- Missing the locator tuple syntax
- Assigning elements directly without initialization
print(page.login_button.is_displayed()) if the button is visible on the page?
from selenium.webdriver.common.by import By
from selenium.webdriver.support.page_factory import PageFactory
class LoginPage:
def __init__(self, driver):
self.driver = driver
PageFactory.init_elements(driver, self)
login_button = (By.ID, 'loginBtn')
page = LoginPage(driver)
print(page.login_button.is_displayed())Solution
Step 1: Understand Page Factory element initialization
PageFactory.init_elements initializes elements sologin_buttonrefers to the actual web element.Step 2: Check the method call
If the button is visible,is_displayed()returns True, so printing it outputs True.Final Answer:
True -> Option AQuick Check:
Visible element's is_displayed() = True [OK]
- Expecting None because locator needs initialization
- Confusing AttributeError due to wrong initialization
- Assuming is_displayed() returns string
from selenium.webdriver.common.by import By
from selenium.webdriver.support.page_factory import PageFactory
class HomePage:
def __init__(self, driver):
self.driver = driver
PageFactory.init_elements(self, self.driver)
submit_button = (By.XPATH, "//button[@id='submit']")
page = HomePage(driver)
page.submit_button.click()Solution
Step 1: Check PageFactory.init_elements arguments
The code usesPageFactory.init_elements(self, self.driver)but arguments must be driver first, then self.Step 2: Confirm correct usage
UsingPageFactory.init_elements(driver, self)fixes the error and allows proper element initialization.Final Answer:
Initialize elements withPageFactory.init_elements(driver, self)-> Option AQuick Check:
init_elements(driver, self) order [OK]
- Wrong argument order for PageFactory.init_elements
- Using wrong decorator name case
- Incorrect PageFactory.init_elements parameters
Solution
Step 1: Understand Page Factory best practice
Page Factory groups elements in a class using locator tuples and initializes them with PageFactory.init_elements.Step 2: Compare with other options
Options B, C, and D do not use Page Factory pattern and lead to less maintainable code.Final Answer:
Declare elements with @FindBy decorators, initialize with PageFactory.init_elements, then use element methods -> Option DQuick Check:
Page Factory = locators + init_elements + class grouping [OK]
- Skipping class and locators for elements
- Using driver calls directly in tests
- Not initializing elements with PageFactory
Ready to prove it?
Pass the test and this pattern moves unknown
