pytest Integration
Build robust Python Selenium tests using pytest fixtures, markers, and plugins.
Selenium 3 & 4 Stable
pytest is Python’s most popular testing framework, offering powerful features like fixtures, parameterization, and plugins that integrate perfectly with Selenium.
Basic Setup
Installation
pip install pytest selenium pytest-htmlProject Structure
project/├── conftest.py # Shared fixtures├── pytest.ini # Configuration├── pages/│ ├── __init__.py│ ├── base_page.py│ └── login_page.py└── tests/ ├── __init__.py ├── conftest.py # Test-specific fixtures ├── test_login.py └── test_search.pypytest.ini Configuration
[pytest]testpaths = testspython_files = test_*.pypython_classes = Test*python_functions = test_*addopts = -v --html=reports/report.html --self-contained-htmlmarkers = smoke: Quick sanity tests regression: Full regression tests slow: Tests that take a long timeFixtures
Basic Driver Fixture
import pytestfrom selenium import webdriverfrom selenium.webdriver.chrome.options import Options
@pytest.fixturedef driver(): """Create a WebDriver instance for each test.""" options = Options() options.add_argument("--start-maximized")
driver = webdriver.Chrome(options=options) yield driver # Provide driver to test driver.quit() # Cleanup after test
@pytest.fixturedef logged_in_driver(driver): """Driver with pre-authenticated session.""" driver.get("https://example.com/login") driver.find_element(By.ID, "username").send_keys("testuser") driver.find_element(By.ID, "password").send_keys("testpass") driver.find_element(By.ID, "login").click() return driverUsing Fixtures in Tests
from selenium.webdriver.common.by import By
def test_valid_login(driver): """Test successful login.""" driver.get("https://example.com/login") driver.find_element(By.ID, "username").send_keys("validuser") driver.find_element(By.ID, "password").send_keys("validpass") driver.find_element(By.ID, "login").click()
assert "/dashboard" in driver.current_url
def test_dashboard_access(logged_in_driver): """Test dashboard with pre-logged-in driver.""" logged_in_driver.get("https://example.com/dashboard") header = logged_in_driver.find_element(By.TAG_NAME, "h1") assert header.text == "Welcome"Fixture Scopes
@pytest.fixture(scope="function") # Default: new for each testdef driver_per_test(): driver = webdriver.Chrome() yield driver driver.quit()
@pytest.fixture(scope="class") # Shared within test classdef driver_per_class(): driver = webdriver.Chrome() yield driver driver.quit()
@pytest.fixture(scope="module") # Shared within moduledef driver_per_module(): driver = webdriver.Chrome() yield driver driver.quit()
@pytest.fixture(scope="session") # Shared across all testsdef driver_per_session(): driver = webdriver.Chrome() yield driver driver.quit()Browser Parametrization
import pytestfrom selenium import webdriver
def pytest_addoption(parser): parser.addoption( "--browser", action="store", default="chrome", help="Browser to run tests: chrome, firefox, edge" )
@pytest.fixturedef driver(request): browser = request.config.getoption("--browser")
if browser == "chrome": driver = webdriver.Chrome() elif browser == "firefox": driver = webdriver.Firefox() elif browser == "edge": driver = webdriver.Edge() else: raise ValueError(f"Unknown browser: {browser}")
driver.maximize_window() yield driver driver.quit()Run with: pytest --browser=firefox
Parameterized Tests
import pytestfrom selenium.webdriver.common.by import By
@pytest.mark.parametrize("username,password,expected", [ ("user1", "pass1", True), ("user2", "pass2", True), ("invalid", "wrong", False),])def test_login(driver, username, password, expected): driver.get("https://example.com/login") driver.find_element(By.ID, "username").send_keys(username) driver.find_element(By.ID, "password").send_keys(password) driver.find_element(By.ID, "login").click()
if expected: assert "/dashboard" in driver.current_url else: assert driver.find_element(By.CLASS_NAME, "error").is_displayed()
@pytest.mark.parametrize("search_term", [ "selenium", "webdriver", "automation",])def test_search(driver, search_term): driver.get("https://example.com") driver.find_element(By.NAME, "q").send_keys(search_term) driver.find_element(By.ID, "search").click()
results = driver.find_elements(By.CLASS_NAME, "result") assert len(results) > 0Markers
import pytest
@pytest.mark.smokedef test_homepage_loads(driver): driver.get("https://example.com") assert "Example" in driver.title
@pytest.mark.regressiondef test_full_checkout_flow(driver): # Full flow test pass
@pytest.mark.slowdef test_large_data_export(driver): # Slow test pass
@pytest.mark.skip(reason="Feature not implemented")def test_new_feature(driver): pass
@pytest.mark.skipif( condition=True, reason="Only run on production")def test_production_only(driver): passRun specific markers: pytest -m smoke
Screenshot on Failure
import pytestimport osfrom datetime import datetime
@pytest.hookimpl(tryfirst=True, hookwrapper=True)def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() setattr(item, f"rep_{report.when}", report)
@pytest.fixturedef driver(request): driver = webdriver.Chrome() driver.maximize_window() yield driver
# Take screenshot on failure if request.node.rep_call.failed: screenshot_dir = "screenshots" os.makedirs(screenshot_dir, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") name = request.node.name filepath = f"{screenshot_dir}/{name}_{timestamp}.png"
driver.save_screenshot(filepath) print(f"Screenshot saved: {filepath}")
driver.quit()Page Object Integration
from selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as EC
class BasePage: def __init__(self, driver): self.driver = driver self.wait = WebDriverWait(driver, 10)
def find(self, locator): return self.wait.until(EC.presence_of_element_located(locator))
def click(self, locator): self.wait.until(EC.element_to_be_clickable(locator)).click()
# pages/login_page.pyfrom selenium.webdriver.common.by import Byfrom pages.base_page import BasePage
class LoginPage(BasePage): USERNAME = (By.ID, "username") PASSWORD = (By.ID, "password") LOGIN_BTN = (By.ID, "login")
def login(self, username, password): self.find(self.USERNAME).send_keys(username) self.find(self.PASSWORD).send_keys(password) self.click(self.LOGIN_BTN)
# tests/test_login.pyfrom pages.login_page import LoginPage
def test_valid_login(driver): driver.get("https://example.com/login") login_page = LoginPage(driver) login_page.login("validuser", "validpass") assert "/dashboard" in driver.current_urlUseful pytest Plugins
| Plugin | Purpose |
|---|---|
| pytest-html | HTML reports |
| pytest-xdist | Parallel execution |
| pytest-rerunfailures | Retry failed tests |
| pytest-timeout | Test timeouts |
| pytest-ordering | Control test order |
Parallel Execution
pip install pytest-xdistpytest -n 4 # Run 4 tests in parallelpytest -n auto # Auto-detect CPU countRetry Failed Tests
pip install pytest-rerunfailurespytest --reruns 3 --reruns-delay 2Next Steps
- Page Object Model - Structure page interactions
- Parallel Execution - Scale your tests
- Data-Driven Testing - External test data
- Screenshots and Videos - Capture test evidence
- TestNG and JUnit - Java framework comparison