Skip to content

Commit 2545c1d

Browse files
committed
Remove references to multiline strings
Add docstring
1 parent 4e821f7 commit 2545c1d

File tree

8 files changed

+244
-81
lines changed

8 files changed

+244
-81
lines changed

CHANGES.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ Unreleased
66
- Update documentation to clarify that `--gherkin-terminal-reporter` needs to be used with `-v` or `-vv`.
77
- Drop compatibility with pytest < 7.0.0.
88
- Continuation of steps using asterisks instead of And/But supported.
9-
- Added `datatable` argument for steps that contain a datatable.
9+
- Added `datatable` argument for steps that contain a datatable (see Data Tables section at https://cucumber.io/docs/gherkin/reference/)
10+
- Added `docstring` argument for steps that contain a docstring (see Doc Strings section at https://cucumber.io/docs/gherkin/reference/).
1011

1112
8.0.0b1
1213
----------

README.rst

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ Example:
561561
assert cucumbers["start"] - cucumbers["eat"] == left
562562
563563
564-
Step Definitions and Accessing the Datatable
564+
Datatable Argument and Accessing the Datatable
565565
--------------------------------------------
566566

567567
The ``datatable`` argument allows you to utilise data tables defined in your Gherkin scenarios
@@ -653,6 +653,78 @@ Full example:
653653
assert users_have_correct_permissions(users, expected_permissions)
654654
655655
656+
Docstring Argument and Accessing the Docstring
657+
---------------------------------------------
658+
659+
The `docstring` argument allows you to access the Gherkin docstring defined in your steps as a multiline string.
660+
The content of the docstring is passed as a single string, with each line separated by `\n`.
661+
Leading indentation are stripped.
662+
663+
For example, the Gherkin docstring:
664+
665+
.. code-block:: gherkin
666+
"""
667+
This is a sample docstring.
668+
It spans multiple lines.
669+
"""
670+
671+
672+
Will be returned as:
673+
674+
.. code-block:: python
675+
"This is a sample docstring.\nIt spans multiple lines."
676+
677+
678+
Full example:
679+
680+
.. code-block:: gherkin
681+
682+
Feature: Docstring
683+
684+
Scenario: Step with docstrings
685+
Given a step has a docstring
686+
"""
687+
This is a given docstring
688+
"""
689+
690+
When a step provides a docstring with lower indentation
691+
"""
692+
This is a when docstring
693+
"""
694+
695+
And this step has no docstring
696+
697+
Then this step has a greater indentation
698+
"""
699+
This is a then docstring
700+
"""
701+
702+
And this step has no docstring
703+
704+
.. code-block:: python
705+
706+
from pytest_bdd import given, when, then
707+
708+
@given("a step has a docstring")
709+
def _(docstring):
710+
print(docstring)
711+
712+
@when("a step provides a docstring with lower indentation")
713+
def _(docstring):
714+
print(docstring)
715+
716+
@then("this step has a greater indentation")
717+
def _(docstring):
718+
print(docstring)
719+
720+
@then("this step has no docstring")
721+
def _():
722+
pass
723+
724+
725+
.. NOTE:: The docstring argument can only be used for steps that have an associated docstring.
726+
Otherwise, an error will be thrown.
727+
656728
Organizing your scenarios
657729
-------------------------
658730

src/pytest_bdd/parser.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99

1010
from .exceptions import StepError
1111
from .gherkin_parser import Background as GherkinBackground
12-
from .gherkin_parser import DataTable, ExamplesTable
12+
from .gherkin_parser import DataTable
1313
from .gherkin_parser import Feature as GherkinFeature
1414
from .gherkin_parser import GherkinDocument
1515
from .gherkin_parser import Scenario as GherkinScenario
1616
from .gherkin_parser import Step as GherkinStep
1717
from .gherkin_parser import Tag as GherkinTag
1818
from .gherkin_parser import get_gherkin_document
19-
from .types import GIVEN, STEP_TYPES, THEN, WHEN
19+
from .types import STEP_TYPES
2020

2121
STEP_PARAM_RE = re.compile(r"<(.+?)>")
2222
COMMENT_RE = re.compile(r"(^|(?<=\s))#")
@@ -172,6 +172,7 @@ def render(self, context: Mapping[str, Any]) -> Scenario:
172172
line_number=step.line_number,
173173
keyword=step.keyword,
174174
datatable=step.datatable,
175+
docstring=step.docstring,
175176
)
176177
for step in self._steps
177178
]
@@ -227,13 +228,21 @@ class Step:
227228
line_number: int
228229
indent: int
229230
keyword: str
231+
docstring: str | None = None
230232
datatable: DataTable | None = None
231233
failed: bool = field(init=False, default=False)
232234
scenario: ScenarioTemplate | None = field(init=False, default=None)
233235
background: Background | None = field(init=False, default=None)
234236

235237
def __init__(
236-
self, name: str, type: str, indent: int, line_number: int, keyword: str, datatable: DataTable | None = None
238+
self,
239+
name: str,
240+
type: str,
241+
indent: int,
242+
line_number: int,
243+
keyword: str,
244+
datatable: DataTable | None = None,
245+
docstring: str | None = None,
237246
) -> None:
238247
"""Initialize a step.
239248
@@ -250,6 +259,7 @@ def __init__(
250259
self.line_number = line_number
251260
self.keyword = keyword
252261
self.datatable = datatable
262+
self.docstring = docstring
253263

254264
def __str__(self) -> str:
255265
"""Return a string representation of the step.
@@ -346,12 +356,6 @@ def parse_steps(self, steps_data: list[GherkinStep]) -> list[Step]:
346356
List[Step]: A list of Step objects.
347357
"""
348358

349-
def get_step_content(_gherkin_step: GherkinStep) -> str:
350-
step_name = strip_comments(_gherkin_step.text)
351-
if _gherkin_step.docstring:
352-
step_name = f"{step_name}\n{_gherkin_step.docstring.content}"
353-
return step_name
354-
355359
if not steps_data:
356360
return []
357361

@@ -360,25 +364,25 @@ def get_step_content(_gherkin_step: GherkinStep) -> str:
360364
raise StepError(
361365
message=f"First step in a scenario or background must start with 'Given', 'When' or 'Then', but got {first_step.keyword}.",
362366
line=first_step.location.line,
363-
line_content=get_step_content(first_step),
367+
line_content=first_step.text,
364368
filename=self.abs_filename,
365369
)
366370

367371
steps = []
368372
current_type = first_step.keyword.lower()
369373
for step in steps_data:
370-
name = get_step_content(step)
371374
keyword = step.keyword.lower()
372375
if keyword in STEP_TYPES:
373376
current_type = keyword
374377
steps.append(
375378
Step(
376-
name=name,
379+
name=strip_comments(step.text),
377380
type=current_type,
378381
indent=step.location.column - 1,
379382
line_number=step.location.line,
380383
keyword=step.keyword.title(),
381384
datatable=step.datatable,
385+
docstring=step.docstring.content if step.docstring else None,
382386
)
383387
)
384388
return steps

src/pytest_bdd/scenario.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ def _execute_step_function(
201201
if step.datatable is not None:
202202
kwargs["datatable"] = step.datatable.raw()
203203

204+
if step.docstring is not None:
205+
kwargs["docstring"] = step.docstring
206+
204207
for arg, value in parsed_args.items():
205208
if arg in converters:
206209
value = converters[arg](value)

tests/datatable/test_datatable.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def test_steps_with_missing_datatables(pytester):
111111
| John | [email protected] | 30 |
112112
| Alice | [email protected] | 25 |
113113
114-
When this step has no data table but tries to use the datatable fixture
114+
When this step has no data table but tries to use the datatable argument
115115
Then an error is thrown
116116
"""
117117
),
@@ -127,7 +127,7 @@ def _(datatable):
127127
print(datatable)
128128
129129
130-
@when("this step has no data table but tries to use the datatable fixture")
130+
@when("this step has no data table but tries to use the datatable argument")
131131
def _(datatable):
132132
print(datatable)
133133

tests/feature/test_background.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
Background:
99
Given foo has a value "bar"
10-
And a background step with multiple lines:
10+
And a background step with docstring:
1111
"""
1212
one
1313
two
@@ -35,9 +35,9 @@ def foo():
3535
return {}
3636
3737
38-
@given(parsers.re(r"a background step with multiple lines:\n(?P<data>.+)", flags=re.DOTALL))
39-
def _(foo, data):
40-
assert data == "one\ntwo"
38+
@given("a background step with docstring:")
39+
def _(foo, docstring):
40+
assert docstring == "one\ntwo"
4141
4242
4343
@given('foo has a value "bar"')

tests/feature/test_multiline.py

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)