See a working example: bistro-e2e-python — Playwright for Python (pytest) E2E tests with QA Sphere integration.
Python (pytest) Integration
This guide covers pytest-specific setup. For install/auth/upload-command reference, see Overview, Auth, and Result Upload.
While the examples below use Playwright for Python, the same approach works with any Python test framework that emits JUnit XML — pytest + Selenium, Robot Framework, plain unit tests, and so on.
Prerequisites
- Python 3.8+ (3.13+ recommended) and a pytest project.
- QAS CLI installed and authenticated — see the Overview. The CLI is distributed as an npm package, so Node.js 18+ is required for the CLI itself (not for your Python project).
Configure JUnit XML output
pytest has built-in JUnit XML support via the --junitxml flag. Wire it up in pyproject.toml so it runs automatically:
# pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = [
"--junitxml=junit-results/results.xml",
]
Or pass it directly:
pytest --junitxml=junit-results/results.xml
With Playwright for Python
To also capture screenshots and traces for failures:
# pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = [
"--screenshot=on",
"--tracing=retain-on-failure",
"--junitxml=junit-results/results.xml",
]
Mark tests with QA Sphere markers
Python function names cannot contain hyphens, so the CLI supports an underscore-separated hyphenless marker. The function name must start with test (the standard pytest convention); the marker can appear immediately after test_ or at the end of the name.
Format: test_PROJECTSEQUENCE_description — where PROJECT is the project code and SEQUENCE is the test case number (minimum 3 digits, zero-padded if needed).
# tests/test_ui_cart.py
from playwright.sync_api import Page
def test_bd023_cart_operations(page: Page, demo_base_url: str) -> None:
"""BD-023: User should see product list on the Checkout page."""
page.goto(f"{demo_base_url}/")
page.locator(".menu-item").first.locator(".add-to-cart").click()
page.locator(".cart-btn").click()
assert page.locator(".checkout-items").is_visible()
def test_bd022_order_with_cash_payment(page: Page, demo_base_url: str) -> None:
"""BD-022: User should place the order successfully with Cash payment."""
# ...
Valid function names:
test_bd023_cart_operations— markerbd023at the start (aftertest_)test_cart_operations_bd023— markerbd023at the end
The underscore marker is matched case-insensitively: test_bd023_... and test_BD023_... both resolve to BD-023.
It's good practice to include the hyphenated form in the docstring for human readability while keeping the underscore marker in the function name for matching.
For all supported marker formats and matching rules, see Result Upload — Test case matching.
Upload results
# Run pytest (writes junit-results/results.xml via pyproject.toml)
pytest --browser chromium -v
# Upload
npx qas-cli junit-upload --project-code BD --attachments junit-results/results.xml
For the full option reference, run-name templates, and modes (new run vs existing), see Result Upload.