A production-ready UI + API test automation framework built with Playwright (Python) and pytest, featuring the Page Object Model, a Service Object layer for API testing, environment-driven configuration, structured logging, failure-artifact capture (screenshots + traces), Docker support, and GitHub Actions pipelines for both pull requests (fast smoke subset) and nightly full regression (cross-browser).
Every module includes docstrings explaining the production concept it demonstrates, not just what the code does — so this repo doubles as a learning reference.
.
├── src/
│ ├── config/settings.py # Environment-driven config (12-factor app)
│ ├── core/base_page.py # Base Page Object (POM)
│ ├── pages/home_page.py # Concrete Page Object example
│ ├── api/client.py # Base HTTP client (requests.Session wrapper)
│ ├── api/posts_service.py # Service Object for the Posts API
│ ├── api/schemas.py # Response contract/schema validation
│ └── utils/logger.py # Centralized structured logging
├── tests/
│ ├── conftest.py # Fixtures + hooks (browser/context/page/api_client)
│ ├── ui/test_home_page.py # UI tests (POM, parametrize, markers)
│ ├── api/test_posts_api.py # API tests (CRUD, schema, negative testing)
│ └── test_hybrid_ui_api.py # Hybrid UI+API test pattern
├── docker/
│ ├── Dockerfile # Based on official Playwright Python image
│ └── docker-compose.yml
├── .github/workflows/
│ ├── pr-checks.yml # Runs on every PR: lint + smoke tests
│ └── nightly.yml # Runs nightly (cron) + manual: full suite, 3 browsers
├── pytest.ini # Pytest config: markers, reporting
├── pyproject.toml # Ruff + mypy config
├── requirements.txt
├── Makefile # Convenience commands
└── .env.example # Documents every configurable env var
| Concept | Where |
|---|---|
| Page Object Model | src/core/base_page.py, src/pages/home_page.py |
| Service Object pattern (API) | src/api/posts_service.py |
| Environment-driven config (12-factor) | src/config/settings.py |
| Schema/contract validation | src/api/schemas.py |
| Fixture scoping (session vs function) | tests/conftest.py |
| Test isolation via browser contexts | tests/conftest.py |
| Failure artifacts (screenshot + trace) via pytest hooks | tests/conftest.py |
| Web-first / auto-retrying assertions | tests/ui/test_home_page.py |
| Parametrized tests | both tests/ui/ and tests/api/ |
| Negative testing | tests/api/test_posts_api.py |
Tiered suites: smoke / regression / nightly markers |
everywhere |
| Hybrid UI+API pattern | tests/test_hybrid_ui_api.py |
| CI: PR vs nightly split | .github/workflows/ |
| Containerized, reproducible runs | docker/ |
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
playwright install --with-deps # downloads browser binaries
cp .env.example .env # optional: customize locally
pytest -m smoke -v # fast subset
pytest -m "smoke or regression" -v
pytest -m "smoke or regression or nightly" -n auto -v # full suite, parallelOr via make:
make install
make browsers
make smokedocker build -f docker/Dockerfile -t playwright-pytest-framework:local .
docker run --rm -e HEADLESS=true -v "$(pwd)/reports:/app/reports" \
playwright-pytest-framework:local -m smokeor with compose:
docker compose -f docker/docker-compose.yml run testsAll configuration is environment-variable driven — see .env.example for
the full list (BASE_URL, API_BASE_URL, BROWSER, HEADLESS,
API_AUTH_TOKEN, etc.). Nothing environment-specific is hardcoded in test
code, so the same image/pipeline can target dev, staging, or prod-like
environments purely by changing env vars.
Declared and enforced (via --strict-markers) in tests/conftest.py:
@pytest.mark.smoke— fast, critical-path checks. Run on every PR.@pytest.mark.regression— broader functional coverage.@pytest.mark.nightly— slow/expensive checks reserved for the nightly job.@pytest.mark.ui/@pytest.mark.api— test type, for selective filtering.
.github/workflows/pr-checks.yml— triggers on every PR tomain. Runsruff+mypy, thenpytest -m smokeagainst Chromium only, for fast feedback. Uploads the HTML/JUnit report and any failure screenshots/traces as build artifacts..github/workflows/nightly.yml— triggers on a cron schedule (02:00 UTC daily) and via manualworkflow_dispatch. Runs the full suite (smoke or regression or nightly) across a Chromium/Firefox/Webkit matrix in parallel (pytest -n auto), uploading per-browser reports. Includes a failure-notification job stub (wire to Slack/Teams/email).
When a UI test fails (locally or in CI), tests/conftest.py's
pytest_runtest_makereport hook automatically saves:
reports/screenshots/<test-id>.pngreports/traces/<test-id>.zip— open with:for a full step-by-step, DOM-snapshot replay of exactly what happened.playwright show-trace reports/traces/<test-id>.zip
- New UI page → add a class to
src/pages/, inheritingBasePage. - New API resource → add a Service Object to
src/api/, and a schema dataclass tosrc/api/schemas.pyif you want contract validation. - New test tier → register the marker in
pytest_configure()intests/conftest.py, then reference it in the relevant workflow's-mexpression