diff --git a/CHANGES.rst b/CHANGES.rst index ec013827..cddd070f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,7 @@ Changelog Unreleased ---------- +- Fix an issue when only the first Step would inject a fixture, while later steps would not be able to. 7.2.0 ---------- diff --git a/src/pytest_bdd/compat.py b/src/pytest_bdd/compat.py index 079f7de0..9b473cdb 100644 --- a/src/pytest_bdd/compat.py +++ b/src/pytest_bdd/compat.py @@ -24,12 +24,17 @@ def inject_fixture(request: FixtureRequest, arg: str, value: Any) -> None: :param arg: argument name :param value: argument value """ - + # Ensure there's a fixture definition for the argument request._fixturemanager._register_fixture( name=arg, func=lambda: value, nodeid=request.node.nodeid, ) + # Note the fixture we just registered will have a lower priority + # if there was already one registered, so we need to force its value + # to the one we want to inject. + fixture_def = request._get_active_fixturedef(arg) + fixture_def.cached_result = (value, None, None) else: diff --git a/tests/steps/test_common.py b/tests/steps/test_common.py index 7108aaab..e3acb05a 100644 --- a/tests/steps/test_common.py +++ b/tests/steps/test_common.py @@ -78,6 +78,48 @@ def _(bar, expected_value): assert bar == "test bar" +def test_step_function_target_fixture_redefined(pytester): + pytester.makefile( + ".feature", + target_fixture=textwrap.dedent( + """\ + Feature: Redefine a target fixture + Scenario: Redefine the target fixture after it has been injected once in the same scenario + Given there is a foo with value "test foo" + Then foo should be "test foo" + Given there is a foo with value "test bar" + Then foo should be "test bar" + """ + ), + ) + pytester.makepyfile( + textwrap.dedent( + """\ + import pytest + from pytest_bdd import given, when, then, scenarios, parsers + from pytest_bdd.utils import dump_obj + + scenarios("target_fixture.feature") + + @given(parsers.parse('there is a foo with value "{value}"'), target_fixture="foo") + def _(value): + return value + + @then(parsers.parse('foo should be "{expected_value}"')) + def _(foo, expected_value): + dump_obj(foo) + assert foo == expected_value + """ + ) + ) + result = pytester.runpytest("-s") + result.assert_outcomes(passed=1) + + [foo1, foo2] = collect_dumped_objects(result) + assert foo1 == "test foo" + assert foo2 == "test bar" + + def test_step_functions_same_parser(pytester): pytester.makefile( ".feature",