Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
77207b9
chore: add pytest-bdd to qase-pytest testing extras
gibiw May 12, 2026
14de659
feat: bootstrap pytest-bdd bridge plugin in qase-pytest
gibiw May 12, 2026
b5571fc
style: drop unused import and tighten docstring in bdd plugin smoke test
gibiw May 12, 2026
e130ad2
feat: add parse_scenario_tags helper for pytest-bdd tag parsing
gibiw May 12, 2026
c020bba
test: tighten parse_scenario_tags coverage and remove brittle dict as…
gibiw May 12, 2026
c3286dd
feat: add format_data_table helper for Gherkin step data tables
gibiw May 12, 2026
245a093
fix: escape backslashes and newlines in markdown table cells
gibiw May 12, 2026
14751ba
feat: add format_docstring helper for Gherkin step docstrings
gibiw May 12, 2026
e71ce75
fix: consolidate imports and handle backticks inside docstrings
gibiw May 12, 2026
004b24e
feat: add build_step helper that converts pytest-bdd Step to Qase Step
gibiw May 12, 2026
2d872ec
feat: add enrich_result_from_scenario helper for scenario metadata
gibiw May 12, 2026
edb2b21
feat: implement pytest_bdd_before_scenario hook
gibiw May 12, 2026
f4cf134
feat: implement pytest_bdd before/after_step happy-path hooks
gibiw May 12, 2026
f8fb5d0
feat: implement step_error, step_func_lookup_error, after_scenario hooks
gibiw May 12, 2026
661e77c
test: cover happy-path after_scenario with no skipped tail
gibiw May 12, 2026
8a5d500
test: add pytester-based integration test for basic pytest-bdd scenario
gibiw May 12, 2026
b129af1
test: integration coverage for failing step and skipped tail
gibiw May 12, 2026
f03fc99
test: integration coverage for step lookup error -> invalid status
gibiw May 12, 2026
903f2d2
test: integration coverage for nested qase.step() under Gherkin step
gibiw May 12, 2026
dc6c14e
test: integration coverage for Scenario Outline parameterization
gibiw May 12, 2026
06daa53
test: integration coverage for DataTable and DocString rendering
gibiw May 12, 2026
44d474f
test: integration coverage for scenario tags and ignore flag
gibiw May 12, 2026
cef4330
test: integration coverage for BDD and plain pytest coexistence
gibiw May 12, 2026
0c41d98
docs: add runnable pytest-bdd example reproducing customer scenario
gibiw May 12, 2026
a48d018
docs: move qase tags from Feature to Scenario in pytest-bdd example
gibiw May 12, 2026
e9710ba
docs: document pytest-bdd integration and bump version to 8.2.0
gibiw May 12, 2026
19b6993
docs: remove Allure mention from pytest-bdd example README
gibiw May 12, 2026
9739231
docs: expand pytest-bdd example with failing, outline, data carriers,…
gibiw May 12, 2026
50230f7
feat: expand pytest-bdd Examples row into individual params
gibiw May 12, 2026
6297d7f
feat: silence PytestUnknownMarkWarning for qase.* gherkin tags
gibiw May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions examples/single/pytest-bdd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# qase-pytest + pytest-bdd example

A runnable demo of the native pytest-bdd integration in qase-pytest.
Exercises every feature of the integration so you can see how each kind
of Gherkin construct is reported in Qase.

## What the example covers

| Feature file | Scenario(s) | Demonstrates |
| --- | --- | --- |
| `login.feature` | Successful login | Basic Given/When/Then with nested `qase.step()` calls — sub-steps appear as children of the Gherkin step. |
| `failing.feature` | A failing assertion in the middle step | A failing Then step. The failed step is marked `failed`; pytest-bdd does not run later steps, but the integration records them as `skipped`. |
| `calculator.feature` | Scenario Outline with 3 example rows | Scenario Outline / Examples — produces three parameterized Qase results for the same scenario. |
| `data_carriers.feature` | Step with a data table and a docstring | DataTable rendered as a markdown table, DocString rendered as a fenced code block — both end up in the step `data` payload. |
| `checkout.feature` | Two scenarios sharing a Background | Background steps run before each scenario and are reported on every scenario's result. Multi-scenario feature with distinct `@qase.id=`, `@qase.suite=` tags per scenario. |

## Recognized scenario tags

Tags must be placed on the `Scenario` line (not the `Feature` line) so
they reach `scenario.tags`:

- `@qase.id=NN` — link to a test case
- `@qase.suite=A.B.C` — override suite chain (dot for nesting)
- `@qase.severity=critical` / `@qase.priority=high` / `@qase.layer=e2e`
- `@qase.ignore` — drop the scenario from the report
- `@qase.muted` — don't let the scenario fail the run

## Run locally (report mode)

```bash
pip install -r requirements.txt
pytest -v
```

5 scenarios execute (one failing on purpose). Inspect the produced JSON
under `build/qase-report/results/`:

- Each result has the scenario name as `title`
- Each result has a suite chain matching the `@qase.suite` tag
- Each result has a list of `steps` mirroring the Gherkin steps in order
- Nested `qase.step(...)` calls (inside step functions) appear as
children of the corresponding Gherkin step
- Failed step has `execution.status: "failed"`; trailing unrun steps
are marked `"skipped"`
- Scenario Outline produces one Qase result per Examples row

## Run against Qase TestOps

Set `mode` to `testops` in `qase.config.json`, and provide credentials
via env:

```bash
export QASE_TESTOPS_API_TOKEN=...
export QASE_TESTOPS_PROJECT=PROJ_CODE
pytest -v
```

The TestOps API mode preserves native Gherkin step structure (keyword
+ name + data) and renders steps with their Given/When/Then keywords
in the Qase UI.
12 changes: 12 additions & 0 deletions examples/single/pytest-bdd/features/calculator.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Feature: Adding numbers

@qase.id=11 @qase.suite=Math.Outline
Scenario Outline: Adding two numbers
Given I have <a> and <b>
Then their sum is <c>

Examples:
| a | b | c |
| 1 | 2 | 3 |
| 5 | 7 | 12 |
| 0 | 0 | 0 |
16 changes: 16 additions & 0 deletions examples/single/pytest-bdd/features/checkout.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Feature: Checkout

Background:
Given the user is signed in
And the cart contains 2 items

@qase.id=13 @qase.suite=Checkout.Success
Scenario: Successful checkout
When the user clicks "Place order"
Then the order is created

@qase.id=14 @qase.suite=Checkout.PaymentDeclined
Scenario: Declined payment
Given the saved card is expired
When the user clicks "Place order"
Then the payment fails with "card_expired"
13 changes: 13 additions & 0 deletions examples/single/pytest-bdd/features/data_carriers.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Feature: Data carriers

@qase.id=12 @qase.suite=API.Payloads
Scenario: Step with a data table and a docstring
Given the following users:
| name | role |
| Alice | admin |
| Bob | user |
When I send the payload:
"""
{"username": "alice", "active": true}
"""
Then the request succeeds
7 changes: 7 additions & 0 deletions examples/single/pytest-bdd/features/failing.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: Math

@qase.id=10 @qase.suite=Math.Failure @qase.severity=major
Scenario: A failing assertion in the middle step
Given a calculator
When I add 2 and 2
Then the result should be 5
7 changes: 7 additions & 0 deletions examples/single/pytest-bdd/features/login.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: Login

@qase.id=1 @qase.suite=Login.Smoke @qase.severity=critical
Scenario: Successful login
Given the user is on the login page
When the user enters valid credentials
Then the user should see the dashboard
12 changes: 12 additions & 0 deletions examples/single/pytest-bdd/qase.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"mode": "report",
"fallback": "off",
"debug": true,
"report": {
"driver": "local",
"connection": {
"path": "./build/qase-report",
"format": "json"
}
}
}
3 changes: 3 additions & 0 deletions examples/single/pytest-bdd/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pytest>=7.4
pytest-bdd>=7.0,<9.0
qase-pytest
13 changes: 13 additions & 0 deletions examples/single/pytest-bdd/tests/test_calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pytest_bdd import scenarios, given, then, parsers

scenarios("../features/calculator.feature")


@given(parsers.parse("I have {a:d} and {b:d}"), target_fixture="numbers")
def numbers(a, b):
return a, b


@then(parsers.parse("their sum is {c:d}"))
def check_sum(numbers, c):
assert sum(numbers) == c
33 changes: 33 additions & 0 deletions examples/single/pytest-bdd/tests/test_checkout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from pytest_bdd import scenarios, given, when, then, parsers

scenarios("../features/checkout.feature")


@given("the user is signed in")
def signed_in():
pass


@given("the cart contains 2 items")
def cart_two_items():
pass


@given("the saved card is expired")
def expired_card():
pass


@when(parsers.parse('the user clicks "{label}"'))
def click(label):
assert label == "Place order"


@then("the order is created")
def order_created():
assert True


@then(parsers.parse('the payment fails with "{reason}"'))
def payment_failure(reason):
assert reason == "card_expired"
21 changes: 21 additions & 0 deletions examples/single/pytest-bdd/tests/test_data_carriers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pytest_bdd import scenarios, given, when, then

scenarios("../features/data_carriers.feature")


@given("the following users:")
def users(datatable=None):
# pytest-bdd injects the data table into the step function when a
# parameter named `datatable` is present; ignore it here — the
# purpose of this example is to see the table land in the Qase report.
pass


@when("I send the payload:")
def payload(docstring=None):
pass


@then("the request succeeds")
def request_ok():
assert True
18 changes: 18 additions & 0 deletions examples/single/pytest-bdd/tests/test_failing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from pytest_bdd import scenarios, given, when, then

scenarios("../features/failing.feature")


@given("a calculator")
def a_calc():
pass


@when("I add 2 and 2")
def add():
pass


@then("the result should be 5")
def assert_five():
assert 2 + 2 == 5
26 changes: 26 additions & 0 deletions examples/single/pytest-bdd/tests/test_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from pytest_bdd import scenarios, given, when, then

from qase.pytest import qase

scenarios("../features/login.feature")


@given("the user is on the login page")
def login_page():
with qase.step("Open login page"):
assert True


@when("the user enters valid credentials")
def valid_credentials():
with qase.step("Enter username and password"):
username = "admin"
password = "password123"
assert username == "admin"
assert password == "password123"


@then("the user should see the dashboard")
def dashboard_visible():
with qase.step("Verify dashboard is visible"):
assert True
54 changes: 54 additions & 0 deletions qase-pytest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,57 @@ See the [examples directory](../examples/) for complete working examples.
## License

Apache License 2.0. See [LICENSE](../LICENSE) for details.

## Using with pytest-bdd

If `pytest-bdd` is installed alongside `qase-pytest`, Gherkin scenarios
are reported with full step hierarchy automatically — no manual
`qase.step()` instrumentation required.

### Feature file

```gherkin
Feature: Login

@qase.id=42 @qase.suite=Login.Smoke
Scenario: Successful login
Given the user is on the login page
When the user enters valid credentials
Then the user should see the dashboard
```

### Test module

```python
from pytest_bdd import scenarios

scenarios("features/login.feature")
```

Scenario name becomes the test title, the Feature becomes the parent suite,
each step is captured with its Given/When/Then keyword. You can still
use `with qase.step("..."):` inside a step function to add nested
sub-steps — they will appear as children of the Gherkin step.

### Recognized scenario tags

Place tags on the `Scenario` line so they reach the plugin via
`scenario.tags`:

| Tag | Effect |
| --- | --- |
| `@qase.id=123` | Link to test case 123 |
| `@qase.id=123,124` | Link to multiple test cases |
| `@qase.project_id.CODE=1,2` | Multi-project link |
| `@qase.ignore` | Skip the scenario from reporting |
| `@qase.muted` | Do not let this scenario fail the run |
| `@qase.suite=A.B.C` | Override suite (dot for nesting) |
| `@qase.severity=critical` | Set severity field |
| `@qase.priority=high` | Set priority field |
| `@qase.layer=e2e` | Set layer field |
| Any other tag | Stored as a free tag on the result |

### Versions

Tested with `pytest-bdd >= 7.0, < 9.0`. `pytest-bdd-ng` is expected to
work but is not officially tested.
15 changes: 15 additions & 0 deletions qase-pytest/changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# qase-pytest 8.2.0

## What's new

- Added native pytest-bdd integration. When `pytest-bdd` is installed,
Gherkin scenarios are reported automatically with full step
hierarchy: scenario name becomes the test title, the feature becomes
the parent suite, Given/When/Then keywords are preserved on each
step, DataTable and DocString contents are captured, Scenario Outline
rows become parameterized results, and a `qase.step()` inside a step
function appears as a sub-step of the Gherkin step.
- Tags on scenarios drive Qase metadata: `@qase.id=`, `@qase.suite=`,
`@qase.severity=`, `@qase.priority=`, `@qase.layer=`, `@qase.ignore`,
`@qase.muted`, `@qase.project_id.CODE=`.

# qase-pytest 8.1.0

## What's new
Expand Down
3 changes: 2 additions & 1 deletion qase-pytest/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "qase-pytest"
version = "8.1.0"
version = "8.2.0"
description = "Qase Pytest Plugin for Qase TestOps and Qase Report"
readme = "README.md"
keywords = ["qase", "pytest", "plugin", "testops", "report", "qase reporting", "test observability"]
Expand Down Expand Up @@ -46,6 +46,7 @@ qase_pytest = "qase.pytest.conftest"
testing = [
"pytest",
"pytest-cov",
"pytest-bdd>=7.0,<9.0",
]

[tool.tox]
Expand Down
Loading
Loading