Skip to main content
Example Project

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 — marker bd023 at the start (after test_)
  • test_cart_operations_bd023 — marker bd023 at the end
note

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.