Skip to content

Commit d4f74d2

Browse files
authored
Merge pull request #503 from pytest-dev/step-def-yield
Allow "yield" in step definitions
2 parents 385e6c9 + 512fd0f commit d4f74d2

File tree

6 files changed

+60
-8
lines changed

6 files changed

+60
-8
lines changed

.pre-commit-config.yaml

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# See https://pre-commit.com/hooks.html for more hooks
33
repos:
44
- repo: https://github.com/psf/black
5-
rev: 21.12b0
5+
# If you update the version here, also update it in tox.ini (py*-pytestlatest-linters)
6+
rev: 22.1.0
67
hooks:
78
- id: black
89
- repo: https://github.com/pycqa/isort
@@ -18,7 +19,7 @@ repos:
1819
- id: check-yaml
1920
- id: check-added-large-files
2021
- repo: https://github.com/asottile/pyupgrade
21-
rev: v2.29.1
22+
rev: v2.31.0
2223
hooks:
2324
- id: pyupgrade
24-
args: [--py36-plus]
25+
args: [--py37-plus]

CHANGES.rst

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This release introduces breaking changes in order to be more in line with the of
1111
- Removed vertical examples for the gherkin compatibility (olegpidsadnyi)
1212
- Step arguments are no longer fixtures (olegpidsadnyi)
1313
- Drop support of python 3.6, pytest 4 (elchupanebrej)
14+
- Step definitions can have "yield" statements again (4.0 release broke it). They will be executed as normal fixtures: code after the yield is executed during teardown of the test. (youtux)
1415

1516

1617

pytest_bdd/cucumber_json.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def _get_result(self, step, report, error_message=False):
6161
result = {"status": "failed", "error_message": str(report.longrepr) if error_message else ""}
6262
elif report.skipped:
6363
result = {"status": "skipped"}
64-
result["duration"] = int(math.floor((10 ** 9) * step["duration"])) # nanosec
64+
result["duration"] = int(math.floor((10**9) * step["duration"])) # nanosec
6565
return result
6666

6767
def _serialize_tags(self, item):

pytest_bdd/scenario.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import typing
1717

1818
import pytest
19-
from _pytest.fixtures import FixtureLookupError
19+
from _pytest.fixtures import FixtureLookupError, call_fixture_func
2020

2121
from . import exceptions
2222
from .feature import get_feature, get_features
@@ -112,8 +112,9 @@ def _execute_step_function(request, scenario, step, step_func):
112112

113113
request.config.hook.pytest_bdd_before_step_call(**kw)
114114
target_fixture = getattr(step_func, "target_fixture", None)
115-
# Execute the step.
116-
return_value = step_func(**kwargs)
115+
116+
# Execute the step as if it was a pytest fixture, so that we can allow "yield" statements in it
117+
return_value = call_fixture_func(fixturefunc=step_func, request=request, kwargs=kwargs)
117118
if target_fixture:
118119
inject_fixture(request, target_fixture, return_value)
119120

tests/feature/test_steps.py

+48
Original file line numberDiff line numberDiff line change
@@ -484,3 +484,51 @@ def test_when_step_validation_error():
484484
result.assert_outcomes(failed=1)
485485
result.stdout.fnmatch_lines(["*test_when_step_validation_error*FAILED"])
486486
assert "INTERNALERROR" not in result.stdout.str()
487+
488+
489+
def test_steps_with_yield(testdir):
490+
"""Test that steps definition containing a yield statement work the same way as
491+
pytest fixture do, that is the code after the yield is executed during teardown."""
492+
493+
testdir.makefile(
494+
".feature",
495+
a="""\
496+
Feature: A feature
497+
498+
Scenario: A scenario
499+
When I setup stuff
500+
Then stuff should be 42
501+
""",
502+
)
503+
testdir.makepyfile(
504+
textwrap.dedent(
505+
"""\
506+
import pytest
507+
from pytest_bdd import given, when, then, scenarios
508+
509+
scenarios("a.feature")
510+
511+
@when("I setup stuff", target_fixture="stuff")
512+
def stuff():
513+
print("Setting up...")
514+
yield 42
515+
print("Tearing down...")
516+
517+
518+
@then("stuff should be 42")
519+
def check_stuff(stuff):
520+
assert stuff == 42
521+
print("Asserted stuff is 42")
522+
523+
"""
524+
)
525+
)
526+
result = testdir.runpytest("-s")
527+
result.assert_outcomes(passed=1)
528+
result.stdout.fnmatch_lines(
529+
[
530+
"*Setting up...*",
531+
"*Asserted stuff is 42*",
532+
"*Tearing down...*",
533+
]
534+
)

tox.ini

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ setenv =
1515
xdist: _PYTEST_MORE_ARGS=-n3 -rfsxX
1616
deps =
1717
pytestlatest: pytest
18+
pytest70: pytest~=7.0.0
1819
pytest62: pytest~=6.2.0
1920
pytest61: pytest~=6.1.0
2021
pytest60: pytest~=6.0.0
@@ -30,7 +31,7 @@ deps =
3031
commands = {env:_PYTEST_CMD:pytest} {env:_PYTEST_MORE_ARGS:} {posargs:-vvl}
3132

3233
[testenv:py310-pytestlatest-linters]
33-
deps = black==21.12b0
34+
deps = black==22.1.0
3435
commands = black --check --verbose setup.py docs pytest_bdd tests
3536

3637
[gh-actions]

0 commit comments

Comments
 (0)