diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7692fd91..56d11f43 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", 3.10.0-rc.2] + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 54eec99d..c2ae6050 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,8 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/psf/black - rev: 21.12b0 + # If you update the version here, also update it in tox.ini (py*-pytestlatest-linters) + rev: 22.1.0 hooks: - id: black - repo: https://github.com/pycqa/isort @@ -18,7 +19,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/asottile/pyupgrade - rev: v2.29.1 + rev: v2.31.0 hooks: - id: pyupgrade - args: [--py36-plus] + args: [--py37-plus] diff --git a/CHANGES.rst b/CHANGES.rst index 4ddf0174..b0cff554 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,8 @@ This release introduces breaking changes in order to be more in line with the of - Removed feature level examples for the gherkin compatibility (olegpidsadnyi) - Removed vertical examples for the gherkin compatibility (olegpidsadnyi) - Step arguments are no longer fixtures (olegpidsadnyi) +- Drop support of python 3.6, pytest 4 (elchupanebrej) +- 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) - Scenario outlines unused example parameter validation is removed (olegpidsadnyi) diff --git a/pyproject.toml b/pyproject.toml index cc1aff8d..2f55c64f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [build-system] -requires = ["setuptools>=42", "wheel"] +requires = ["setuptools>=58", "wheel"] build-backend = "setuptools.build_meta" [tool.black] line-length = 120 -target-version = ['py36', 'py37', 'py38'] +target-version = ["py37", "py38", "py39", "py310"] [tool.isort] profile = "black" diff --git a/pytest_bdd/cucumber_json.py b/pytest_bdd/cucumber_json.py index 07b5e6ea..3102a503 100644 --- a/pytest_bdd/cucumber_json.py +++ b/pytest_bdd/cucumber_json.py @@ -61,7 +61,7 @@ def _get_result(self, step, report, error_message=False): result = {"status": "failed", "error_message": str(report.longrepr) if error_message else ""} elif report.skipped: result = {"status": "skipped"} - result["duration"] = int(math.floor((10 ** 9) * step["duration"])) # nanosec + result["duration"] = int(math.floor((10**9) * step["duration"])) # nanosec return result def _serialize_tags(self, item): diff --git a/pytest_bdd/scenario.py b/pytest_bdd/scenario.py index 43aacd6a..38ad4033 100644 --- a/pytest_bdd/scenario.py +++ b/pytest_bdd/scenario.py @@ -16,7 +16,7 @@ import typing import pytest -from _pytest.fixtures import FixtureLookupError +from _pytest.fixtures import FixtureLookupError, call_fixture_func from . import exceptions from .feature import get_feature, get_features @@ -112,8 +112,9 @@ def _execute_step_function(request, scenario, step, step_func): request.config.hook.pytest_bdd_before_step_call(**kw) target_fixture = getattr(step_func, "target_fixture", None) - # Execute the step. - return_value = step_func(**kwargs) + + # Execute the step as if it was a pytest fixture, so that we can allow "yield" statements in it + return_value = call_fixture_func(fixturefunc=step_func, request=request, kwargs=kwargs) if target_fixture: inject_fixture(request, target_fixture, return_value) diff --git a/setup.cfg b/setup.cfg index f65f9aa6..90403b8e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,20 +19,20 @@ classifiers = Topic :: Software Development :: Libraries Topic :: Utilities Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 [options] -python_requires = >=3.6 +python_requires = >=3.7 install_requires = glob2 Mako parse parse_type py - pytest>=4.3 + pytest>=5.0 tests_require = tox packages = pytest_bdd diff --git a/tests/feature/test_steps.py b/tests/feature/test_steps.py index 63ed1498..1cb72b58 100644 --- a/tests/feature/test_steps.py +++ b/tests/feature/test_steps.py @@ -484,3 +484,51 @@ def test_when_step_validation_error(): result.assert_outcomes(failed=1) result.stdout.fnmatch_lines(["*test_when_step_validation_error*FAILED"]) assert "INTERNALERROR" not in result.stdout.str() + + +def test_steps_with_yield(testdir): + """Test that steps definition containing a yield statement work the same way as + pytest fixture do, that is the code after the yield is executed during teardown.""" + + testdir.makefile( + ".feature", + a="""\ +Feature: A feature + + Scenario: A scenario + When I setup stuff + Then stuff should be 42 +""", + ) + testdir.makepyfile( + textwrap.dedent( + """\ + import pytest + from pytest_bdd import given, when, then, scenarios + + scenarios("a.feature") + + @when("I setup stuff", target_fixture="stuff") + def stuff(): + print("Setting up...") + yield 42 + print("Tearing down...") + + + @then("stuff should be 42") + def check_stuff(stuff): + assert stuff == 42 + print("Asserted stuff is 42") + + """ + ) + ) + result = testdir.runpytest("-s") + result.assert_outcomes(passed=1) + result.stdout.fnmatch_lines( + [ + "*Setting up...*", + "*Asserted stuff is 42*", + "*Tearing down...*", + ] + ) diff --git a/tests/scripts/test_generate.py b/tests/scripts/test_generate.py index fc8eeec4..12dea062 100644 --- a/tests/scripts/test_generate.py +++ b/tests/scripts/test_generate.py @@ -176,9 +176,6 @@ def test_unicode_characters(testdir, monkeypatch): ), ) - if sys.version_info < (3, 7): - monkeypatch.setenv("PYTHONIOENCODING", "utf-8") - result = testdir.run("pytest-bdd", "generate", "unicode_characters.feature") expected_output = textwrap.dedent( '''\ diff --git a/tox.ini b/tox.ini index 219b1cbb..25fe0e2e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,12 @@ [tox] distshare = {homedir}/.tox/distshare -envlist = py38-pytestlatest-linters, - py39-pytest{43,44,45,46,50,51,52,53,54,60,61,62, latest}-coverage, - py{36,37,38,310}-pytestlatest-coverage, - py39-pytestlatest-xdist-coverage +envlist = py310-pytestlatest-linters, + ; python 3.10 is only supported by pytest >= 6.2.5: + py310-pytest{62,70,latest}-coverage, + ; the rest of pytest runs need to use an older python: + py39-pytest{50,51,52,53,54,60,61}-coverage, + py{37,38,39}-pytestlatest-coverage, + py310-pytestlatest-xdist-coverage skip_missing_interpreters = true [testenv] @@ -12,6 +15,7 @@ setenv = xdist: _PYTEST_MORE_ARGS=-n3 -rfsxX deps = pytestlatest: pytest + pytest70: pytest~=7.0.0 pytest62: pytest~=6.2.0 pytest61: pytest~=6.1.0 pytest60: pytest~=6.0.0 @@ -20,24 +24,18 @@ deps = pytest52: pytest~=5.2.0 pytest51: pytest~=5.1.0 pytest50: pytest~=5.0.0 - pytest46: pytest~=4.6.0 - pytest45: pytest~=4.5.0 - pytest44: pytest~=4.4.0 - pytest43: pytest~=4.3.0 coverage: coverage xdist: pytest-xdist -r{toxinidir}/requirements-testing.txt commands = {env:_PYTEST_CMD:pytest} {env:_PYTEST_MORE_ARGS:} {posargs:-vvl} -; Black doesn't support >py38 now -[testenv:py38-pytestlatest-linters] -deps = black +[testenv:py310-pytestlatest-linters] +deps = black==22.1.0 commands = black --check --verbose setup.py docs pytest_bdd tests [gh-actions] python = - 3.6: py36 3.7: py37 3.8: py38 3.9: py39