End-to-end tests for demoblaze.com written in Python with Selenium 4, pytest and the Page Object Model. Tests run in parallel via pytest-xdist, inside a Docker image with a pinned Chrome, and produce an Allure report on every push to main.
demoblaze-ci-framework/
├── pages/
│ ├── base_page.py
│ ├── home_page.py
│ ├── product_page.py
│ └── cart_page.py
├── tests/
│ ├── test_product_browsing.py
│ ├── test_cart.py
│ ├── test_contact.py
│ └── test_purchase_flow.py
├── utils/
│ ├── logger.py
│ └── screenshot.py
├── conftest.py
├── pytest.ini
├── Dockerfile
├── docker-compose.yml
└── .github/workflows/test.yml
The driver fixture in conftest.py is function-scoped: every test gets its own Chrome, which it quits at teardown. That means zero shared state across xdist workers, so pytest -n auto is safe by construction rather than by careful test ordering. A browser-per-test costs a couple of seconds of startup each, but the suite is small and I/O-bound on demoblaze, so the win from running N tests at once dwarfs it.
Selenium's selenium-manager fetches a chromedriver that matches whatever Chrome it finds. Chrome itself is what drifts: a laptop auto-updates, a CI runner ships a different build. The Dockerfile installs google-chrome-stable from Google's apt repo, so local and CI runs use the same Chrome and the same driver. shm_size: 2gb in docker-compose.yml stops headless Chrome crashing on the default 64 MB /dev/shm. allure-results/ and screenshots/ are volume-mounted, so results land on the host.
pytest-html gives one flat table per run. Allure gives:
- per-step timelines and durations for each test
- attachments: the
pytest_runtest_makereporthook inutils/screenshot.pysaves a screenshot on failure and attaches it to the failing test - history and retries across runs, plus flaky-test detection
- categorization of failures (product defect vs test defect vs broken environment)
Tradeoff: rendering the report needs Java and the Allure CLI, whereas pytest-html is a single HTML file. pytest.ini writes raw results to allure-results/ (--alluredir, cleaned on each run); rendering is a separate step, done locally with allure serve and in CI with allure generate.
python -m venv .venv
source .venv/bin/activate # PowerShell: .venv\Scripts\Activate.ps1
pip install -r requirements.txtChrome must be installed. chromedriver is not needed; selenium-manager downloads it on first run.
To view reports locally, install the Allure CLI (npm i -g allure-commandline, requires Java 8+).
pytest # serial, headless
pytest -n auto # parallel, one worker per CPU core
HEADLESS=0 pytest # watch the browser (PowerShell: $env:HEADLESS="0"; pytest)
allure serve allure-resultsOr in Docker, which needs nothing but Docker installed:
docker compose up --build
allure serve allure-results # results are volume-mounted back to the host.github/workflows/test.yml runs on every push to main and on manual dispatch:
docker compose builddocker compose run --rm testswithcontinue-on-error: true, so a red suite still reaches the reporting stepsallure generate allure-results -o allure-report --cleanvia the npm-installed CLI- Upload
allure-resultsandallure-reportas artifacts (14-day retention) - Fail the job if the test step's outcome was
failure
Download the allure-report artifact from the run summary and open index.html (serve it over HTTP, e.g. python -m http.server, since the report loads JSON via fetch).
Overview page of the report generated by GitHub Actions run 35280472027, downloaded from the allure-report artifact.
To regenerate: run the suite, then allure generate allure-results -o allure-report --clean, open the report and screenshot the Overview page to docs/allure-report.png.
