Skip to content

Commit

Permalink
Merge branch 'master' into test-177
Browse files Browse the repository at this point in the history
  • Loading branch information
jsa34 authored Dec 2, 2024
2 parents 6e1f54d + 6a2b2fb commit 633ee28
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 53 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Added
Changed
+++++++
* Step arguments ``"datatable"`` and ``"docstring"`` are now reserved, and they can't be used as step argument names.
* Scenario ``description`` field is now set for JSON output

Deprecated
++++++++++
Expand Down
118 changes: 85 additions & 33 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -565,39 +565,6 @@ Example:
assert datatable[1][1] in ["user1", "user2"]
Rules
-----

In Gherkin, `Rules` allow you to group related scenarios or examples under a shared context.
This is useful when you want to define different conditions or behaviours
for multiple examples that follow a similar structure.
You can use either ``Scenario`` or ``Example`` to define individual cases, as they are aliases and function identically.

Additionally, **tags** applied to a rule will be automatically applied to all the **examples or scenarios**
under that rule, making it easier to organize and filter tests during execution.

Example:

.. code-block:: gherkin
Feature: Rules and examples
@feature_tag
Rule: A rule for valid cases
@rule_tag
Example: Valid case 1
Given I have a valid input
When I process the input
Then the result should be successful
Rule: A rule for invalid cases
Example: Invalid case
Given I have an invalid input
When I process the input
Then the result should be an error
Scenario Outlines with Multiple Example Tables
----------------------------------------------

Expand Down Expand Up @@ -663,6 +630,91 @@ only the examples under the "Positive results" table will be executed, and the "
pytest -k "positive"
Handling Empty Example Cells
----------------------------

By default, empty cells in the example tables are interpreted as empty strings ("").
However, there may be cases where it is more appropriate to handle them as ``None``.
In such scenarios, you can use a converter with the ``parsers.re`` parser to define a custom behavior for empty values.

For example, the following code demonstrates how to use a custom converter to return ``None`` when an empty cell is encountered:

.. code-block:: gherkin
# content of empty_example_cells.feature
Feature: Handling empty example cells
Scenario Outline: Using converters for empty cells
Given I am starting lunch
Then there are <start> cucumbers
Examples:
| start |
| |
.. code-block:: python
from pytest_bdd import then, parsers
# Define a converter that returns None for empty strings
def empty_to_none(value):
return None if value.strip() == "" else value
@given("I am starting lunch")
def _():
pass
@then(
parsers.re("there are (?P<start>.*?) cucumbers"),
converters={"start": empty_to_none}
)
def _(start):
# Example assertion to demonstrate the conversion
assert start is None
Here, the `start` cell in the example table is empty.
When the ``parsers.re`` parser is combined with the ``empty_to_none`` converter,
the empty cell will be converted to ``None`` and can be handled accordingly in the step definition.


Rules
-----

In Gherkin, `Rules` allow you to group related scenarios or examples under a shared context.
This is useful when you want to define different conditions or behaviours
for multiple examples that follow a similar structure.
You can use either ``Scenario`` or ``Example`` to define individual cases, as they are aliases and function identically.

Additionally, **tags** applied to a rule will be automatically applied to all the **examples or scenarios**
under that rule, making it easier to organize and filter tests during execution.

Example:

.. code-block:: gherkin
Feature: Rules and examples
@feature_tag
Rule: A rule for valid cases
@rule_tag
Example: Valid case 1
Given I have a valid input
When I process the input
Then the result should be successful
Rule: A rule for invalid cases
Example: Invalid case
Given I have an invalid input
When I process the input
Then the result should be an error
Datatables
----------

Expand Down
2 changes: 1 addition & 1 deletion src/pytest_bdd/cucumber_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def stepmap(step: dict[str, Any]) -> dict[str, Any]:
"id": report.item["name"],
"name": scenario["name"],
"line": scenario["line_number"],
"description": "",
"description": scenario["description"],
"tags": self._serialize_tags(scenario),
"type": "scenario",
"steps": [stepmap(step) for step in scenario["steps"]],
Expand Down
1 change: 1 addition & 0 deletions src/pytest_bdd/reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def serialize(self) -> dict[str, Any]:
"name": scenario.name,
"line_number": scenario.line_number,
"tags": sorted(scenario.tags),
"description": scenario.description,
"feature": {
"keyword": feature.keyword,
"name": feature.name,
Expand Down
41 changes: 22 additions & 19 deletions tests/feature/test_cucumber_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ def test_step_trace(pytester):
"""
@feature-tag
Feature: One passing scenario, one failing scenario
This is a feature description
@scenario-passing-tag
Scenario: Passing
This is a scenario description
Given a passing step
And some other passing step
Expand Down Expand Up @@ -116,108 +119,108 @@ def test_passing_outline():
assert result.ret
expected = [
{
"description": "",
"description": "This is a feature description",
"elements": [
{
"description": "",
"description": "This is a scenario description",
"id": "test_passing",
"keyword": "Scenario",
"line": 5,
"line": 6,
"name": "Passing",
"steps": [
{
"keyword": "Given",
"line": 6,
"line": 9,
"match": {"location": ""},
"name": "a passing step",
"result": {"status": "passed", "duration": OfType(int)},
},
{
"keyword": "And",
"line": 7,
"line": 10,
"match": {"location": ""},
"name": "some other passing step",
"result": {"status": "passed", "duration": OfType(int)},
},
],
"tags": [{"name": "scenario-passing-tag", "line": 4}],
"tags": [{"name": "scenario-passing-tag", "line": 5}],
"type": "scenario",
},
{
"description": "",
"id": "test_failing",
"keyword": "Scenario",
"line": 10,
"line": 13,
"name": "Failing",
"steps": [
{
"keyword": "Given",
"line": 11,
"line": 14,
"match": {"location": ""},
"name": "a passing step",
"result": {"status": "passed", "duration": OfType(int)},
},
{
"keyword": "And",
"line": 12,
"line": 15,
"match": {"location": ""},
"name": "a failing step",
"result": {"error_message": OfType(str), "status": "failed", "duration": OfType(int)},
},
],
"tags": [{"name": "scenario-failing-tag", "line": 9}],
"tags": [{"name": "scenario-failing-tag", "line": 12}],
"type": "scenario",
},
{
"description": "",
"keyword": "Scenario Outline",
"tags": [{"line": 14, "name": "scenario-outline-passing-tag"}],
"tags": [{"line": 17, "name": "scenario-outline-passing-tag"}],
"steps": [
{
"line": 16,
"line": 19,
"match": {"location": ""},
"result": {"status": "passed", "duration": OfType(int)},
"keyword": "Given",
"name": "type str and value hello",
}
],
"line": 15,
"line": 18,
"type": "scenario",
"id": "test_passing_outline[str-hello]",
"name": "Passing outline",
},
{
"description": "",
"keyword": "Scenario Outline",
"tags": [{"line": 14, "name": "scenario-outline-passing-tag"}],
"tags": [{"line": 17, "name": "scenario-outline-passing-tag"}],
"steps": [
{
"line": 16,
"line": 19,
"match": {"location": ""},
"result": {"status": "passed", "duration": OfType(int)},
"keyword": "Given",
"name": "type int and value 42",
}
],
"line": 15,
"line": 18,
"type": "scenario",
"id": "test_passing_outline[int-42]",
"name": "Passing outline",
},
{
"description": "",
"keyword": "Scenario Outline",
"tags": [{"line": 14, "name": "scenario-outline-passing-tag"}],
"tags": [{"line": 17, "name": "scenario-outline-passing-tag"}],
"steps": [
{
"line": 16,
"line": 19,
"match": {"location": ""},
"result": {"status": "passed", "duration": OfType(int)},
"keyword": "Given",
"name": "type float and value 1.0",
}
],
"line": 15,
"line": 18,
"type": "scenario",
"id": "test_passing_outline[float-1.0]",
"name": "Passing outline",
Expand Down
4 changes: 4 additions & 0 deletions tests/feature/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def _(cucumbers, left):
"keyword": "Scenario",
"line_number": 5,
"name": "Passing",
"description": "",
"steps": [
{
"duration": OfType(float),
Expand Down Expand Up @@ -155,6 +156,7 @@ def _(cucumbers, left):
"keyword": "Scenario",
"line_number": 10,
"name": "Failing",
"description": "",
"steps": [
{
"duration": OfType(float),
Expand Down Expand Up @@ -192,6 +194,7 @@ def _(cucumbers, left):
"keyword": "Scenario Outline",
"line_number": 14,
"name": "Outlined",
"description": "",
"steps": [
{
"duration": OfType(float),
Expand Down Expand Up @@ -237,6 +240,7 @@ def _(cucumbers, left):
"keyword": "Scenario Outline",
"line_number": 14,
"name": "Outlined",
"description": "",
"steps": [
{
"duration": OfType(float),
Expand Down

0 comments on commit 633ee28

Please sign in to comment.