A modular, scalable framework using Python, Selenium, and the Page Object Model (POM) pattern.
📦framework/
├── 📂base/ # Core framework components
│ ├── base_page.py # High-level page interactions
│ ├── selenium_wrapper.py # Enhanced Selenium methods
│ └── web_driver_factory.py # Driver setup (local/remote)
│
├── 📂pages/ # Page Object Classes
│ ├── login_page.py # Example: LoginPage
│ ├── home_page.py # Example: HomePage
│ └── ... # Other pages
│
├── 📂locators/ # Locator definitions
│ ├── common.py # Shared locators (e.g., header/footer)
│ └── dynamic.py # Locators with variables
│
├── 📂tests/ # Test scripts
│ ├── test_login.py
│ └── ...
│
└── 📂utils/ # Helpers
├── logger.py # Custom logging
└── config_reader.py # Environment configs
Wraps Selenium methods with built-in waits and logging:
class SeleniumWrapper:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def click(self, locator):
"""Clicks an element after ensuring it's clickable."""
self.wait.until(EC.element_to_be_clickable(locator)).click()
Handles driver instantiation:
class WebDriverFactory:
@staticmethod
def get_driver(browser="chrome"):
if browser == "chrome":
return webdriver.Chrome()
# Add other browsers (Firefox, Edge)
Inherits SeleniumWrapper
for page-specific workflows:
class BasePage(SeleniumWrapper):
def __init__(self, driver):
super().__init__(driver)
Defined directly in page classes:
# pages/login_page.py
class LoginPage(BasePage):
USERNAME = (By.ID, "username", "Username Field")
PASSWORD = (By.ID, "password", "Password Field")
Stored in locators/common.py
:
# locators/common.py
HEADER = (By.TAG_NAME, "header", "Global Header")
Generated at runtime in locators/dynamic.py
:
# locators/dynamic.py
def button_by_text(text):
return (By.XPATH, f"//button[text()='{text}']", f"Button: {text}")
# pages/login_page.py
from base.base_page import BasePage
from locators.common import HEADER
class LoginPage(BasePage):
USERNAME = (By.ID, "username", "Username Field")
def login(self, username, password):
self.type(self.USERNAME, username)
self.click(HEADER) # Reused from common.py
# tests/test_login.py
def test_login():
driver = WebDriverFactory.get_driver()
login_page = LoginPage(driver)
login_page.login("user", "pass")
- Separation of Concerns:
SeleniumWrapper
: Low-level browser interactions.BasePage
: Page workflows.- Locators: Centralized for reusability.
- Dynamic Locators: Generate locators with variables.
- Easy Scaling: Add new pages without duplicating code.
Here’s a polished README.md
section for setting up and running tests with Python, Selenium, and Allure reports, including prerequisites:
-
Install Python (v3.9+ recommended):
Download and install Python from python.org. Verify installation:python --version
-
Install Java (Required for Allure):
Download Java JDK from Oracle or use OpenJDK. Verify installation:java -version
-
Install Allure Commandline:
Follow the official Allure installation guide for your OS.
Verify installation:allure --version
-
Create and activate a virtual environment (recommended):
python -m venv venv source venv/bin/activate # Linux/Mac venv\Scripts\activate # Windows
-
Install dependencies from
requirements.txt
:pip install -r requirements.txt
-
Execute tests with Pytest and generate Allure results:
pytest tests/ --alluredir=reports
-
View Allure Report (auto-opens in browser):
allure serve reports
Q: Why separate SeleniumWrapper
and BasePage
?
A: To reuse Selenium methods in non-page classes (e.g., utilities).
Q: How to add a new page?
- Create
pages/new_page.py
. - Define locators in the class or
locators/
. - Inherit
BasePage
.
- Fork the repo.
- Add tests for new features.
- Submit a PR with updates to
README.md
.
Test sites used -
https://the-internet.herokuapp.com https://demoqa.com