1818import os
1919import re
2020from collections .abc import Iterable , Iterator
21+ from inspect import signature
2122from typing import TYPE_CHECKING , Any , Callable , TypeVar , cast
2223
2324import pytest
2829from .compat import getfixturedefs , inject_fixture
2930from .feature import get_feature , get_features
3031from .steps import StepFunctionContext , get_step_fixture_name
31- from .utils import CONFIG_STACK , get_args , get_caller_module_locals , get_caller_module_path , identity
32+ from .utils import CONFIG_STACK , get_caller_module_locals , get_caller_module_path , get_required_args , identity
3233
3334if TYPE_CHECKING :
3435 from _pytest .mark .structures import ParameterSet
@@ -201,6 +202,9 @@ def _execute_step_function(
201202) -> None :
202203 """Execute step function."""
203204 __tracebackhide__ = True
205+
206+ func_sig = signature (context .step_func )
207+
204208 kw = {
205209 "request" : request ,
206210 "feature" : scenario .feature ,
@@ -210,24 +214,31 @@ def _execute_step_function(
210214 "step_func_args" : {},
211215 }
212216 request .config .hook .pytest_bdd_before_step (** kw )
213- args = get_args (context .step_func )
214217
215218 try :
216- kwargs = parse_step_arguments (step = step , context = context )
219+ parsed_args = parse_step_arguments (step = step , context = context )
217220
218- if step . datatable is not None :
219- kwargs [ STEP_ARGUMENT_DATATABLE ] = step . datatable . raw ()
221+ # Filter out the arguments that are not in the function signature
222+ kwargs = { k : v for k , v in parsed_args . items () if k in func_sig . parameters }
220223
221- if step .docstring is not None :
224+ if STEP_ARGUMENT_DATATABLE in func_sig .parameters and step .datatable is not None :
225+ kwargs [STEP_ARGUMENT_DATATABLE ] = step .datatable .raw ()
226+ if STEP_ARGUMENT_DOCSTRING in func_sig .parameters and step .docstring is not None :
222227 kwargs [STEP_ARGUMENT_DOCSTRING ] = step .docstring
223228
224- kwargs = {arg : kwargs [arg ] if arg in kwargs else request .getfixturevalue (arg ) for arg in args }
229+ # Fill the missing arguments requesting the fixture values
230+ kwargs |= {
231+ arg : request .getfixturevalue (arg ) for arg in get_required_args (context .step_func ) if arg not in kwargs
232+ }
225233
226234 kw ["step_func_args" ] = kwargs
227235
228236 request .config .hook .pytest_bdd_before_step_call (** kw )
229- # Execute the step as if it was a pytest fixture, so that we can allow "yield" statements in it
237+
238+ # Execute the step as if it was a pytest fixture using `call_fixture_func`,
239+ # so that we can allow "yield" statements in it
230240 return_value = call_fixture_func (fixturefunc = context .step_func , request = request , kwargs = kwargs )
241+
231242 except Exception as exception :
232243 request .config .hook .pytest_bdd_step_error (exception = exception , ** kw )
233244 raise
@@ -280,7 +291,7 @@ def decorator(*args: Callable[P, T]) -> Callable[P, T]:
280291 "scenario function can only be used as a decorator. Refer to the documentation."
281292 )
282293 [fn ] = args
283- func_args = get_args (fn )
294+ func_args = get_required_args (fn )
284295
285296 def scenario_wrapper (request : FixtureRequest , _pytest_bdd_example : dict [str , str ]) -> Any :
286297 __tracebackhide__ = True
0 commit comments