Setup Pytest Automation Framework
When to Use
- Creating a new Pytest test automation project
- Configuring Pytest fixtures for test setup/teardown
- Adding parallel execution with pytest-xdist
- Setting up HTML and Allure reporting
- Organizing tests with conftest.py
Procedure
1. Install Dependencies
pip install pytest==7.4.0 pytest-xdist==3.3.1 pytest-html==3.2.0 \
pytest-metadata==2.0.4 allure-pytest==2.13.2 python-dotenv==1.0.0 \
playwright==1.36.0 pytest-playwright==0.3.0
2. Create pytest.ini
[pytest]
minversion = 7.0
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
markers =
smoke: Smoke tests
regression: Regression tests
ui: UI tests
playwright: Playwright tests
mobile: Mobile tests
multi_browser: Cross-browser tests
addopts =
-v
--tb=short
--strict-markers
--html=reports/report.html
--self-contained-html
3. Create conftest.py
import pytest
import os
from dotenv import load_dotenv
from playwright.sync_api import sync_playwright
from factories.browser_factory import PlaywrightBrowserFactory, PlaywrightPage
from config.settings import config
from utils.logger import get_logger
load_dotenv()
logger = get_logger(__name__)
# ── Session Fixtures ──────────────────────────────────────────
@pytest.fixture(scope="session")
def playwright_instance():
"""Start Playwright once for the entire session."""
pw = sync_playwright().start()
yield pw
pw.stop()
# ── Function Fixtures ─────────────────────────────────────────
@pytest.fixture(scope="function")
def browser_context():
"""Fresh browser context per test."""
playwright, browser, context = PlaywrightBrowserFactory.create_browser_context(
browser_name=config.browser_type,
headless=config.headless
)
yield context
context.close()
browser.close()
playwright.stop()
@pytest.fixture(scope="function")
def page(browser_context):
"""Provide a Playwright page."""
pg = browser_context.new_page()
yield PlaywrightPage(pg)
pg.close()
@pytest.fixture(scope="function")
def mobile_page():
"""Mobile browser page (iPhone 12)."""
playwright, browser, context = PlaywrightBrowserFactory.create_mobile_browser("iPhone 12")
pg = context.new_page()
yield PlaywrightPage(pg)
pg.close()
context.close()
browser.close()
playwright.stop()
@pytest.fixture(params=["chromium", "firefox", "webkit"])
def multi_browser_page(request):
"""Parametrized cross-browser fixture."""
playwright, browser, context = PlaywrightBrowserFactory.create_browser_context(request.param)
pg = context.new_page()
yield PlaywrightPage(pg)
pg.close()
context.close()
browser.close()
playwright.stop()
# ── Pytest Hooks ──────────────────────────────────────────────
def pytest_configure(config):
config.addinivalue_line("markers", "smoke: smoke tests")
config.addinivalue_line("markers", "regression: regression tests")
def pytest_collection_modifyitems(config, items):
for item in items:
if "ui" in str(item.fspath):
item.add_marker(pytest.mark.ui)
if "playwright" in str(item.fspath):
item.add_marker(pytest.mark.playwright)
def pytest_runtest_logreport(report):
if report.when == "call":
if report.outcome == "passed":
logger.info(f"PASS {report.nodeid}")
elif report.outcome == "failed":
logger.error(f"FAIL {report.nodeid}")
def pytest_sessionfinish(session, exitstatus):
logger.info(f"Session finished — exit status: {exitstatus}")
4. config/settings.py
import os
from dataclasses import dataclass
from dotenv import load_dotenv
load_dotenv()
@dataclass
class Config:
browser_type: str = os.getenv("BROWSER", "chromium")
headless: bool = os.getenv("HEADLESS", "True").lower() == "true"
base_url: str = os.getenv("BASE_URL", "https://app.example.com")
timeout: int = int(os.getenv("TIMEOUT", "10000"))
config = Config()
5. Run Commands
# All tests
pytest tests/
# By marker
pytest tests/ -m smoke
pytest tests/ -m "playwright and not mobile"
# Parallel (4 workers)
pytest tests/ -n 4
# Specific browser
BROWSER=firefox pytest tests/
# Debug mode (headed)
HEADLESS=False pytest tests/
# With Allure report
pytest tests/ --alluredir=reports/allure-results
allure serve reports/allure-results
Best Practices
- Session fixtures for expensive resources (browser launch)
- Function fixtures for test isolation (fresh context per test)
- Markers for selective test execution in CI/CD
- Hooks for logging, auto-marking, and reporting