Skip to content

Commit

Permalink
Update scenario.py
Browse files Browse the repository at this point in the history
Add regression tested code to not break when Args not in the method signature are present
  • Loading branch information
jsa34 authored Nov 19, 2024
1 parent 7124f1a commit a9ce43f
Showing 1 changed file with 40 additions and 21 deletions.
61 changes: 40 additions & 21 deletions src/pytest_bdd/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import os
import re
from collections.abc import Iterable, Iterator
from inspect import signature
from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast

import pytest
Expand Down Expand Up @@ -177,6 +178,38 @@ def _execute_step_function(
) -> None:
"""Execute step function."""
__tracebackhide__ = True

func_sig = signature(context.step_func)
converters = context.converters

def _get_parsed_arguments():
"""Parse and convert step arguments."""
parsed_args = context.parser.parse_arguments(step.name)
if parsed_args is None:
raise ValueError(
f"Unexpected `NoneType` returned from parse_arguments(...) in parser: {context.parser!r}"
)
kwargs = {}
for arg, value in parsed_args.items():
param = func_sig.parameters.get(arg)
if param:
if arg in converters:
value = converters[arg](value)
kwargs[arg] = value
return kwargs

def _get_argument_values(kwargs):
"""Get default values or request fixture values for missing arguments."""
for arg in get_args(context.step_func):
if arg not in kwargs:
param = func_sig.parameters.get(arg)
if param:
if param.default != param.empty:
kwargs[arg] = param.default
else:
kwargs[arg] = request.getfixturevalue(arg)
return kwargs

kw = {
"request": request,
"feature": scenario.feature,
Expand All @@ -188,37 +221,23 @@ def _execute_step_function(

request.config.hook.pytest_bdd_before_step(**kw)

# Get the step argument values.
converters = context.converters
kwargs = {}
args = get_args(context.step_func)

try:
parsed_args = context.parser.parse_arguments(step.name)
assert parsed_args is not None, (
f"Unexpected `NoneType` returned from " f"parse_arguments(...) in parser: {context.parser!r}"
)

for arg, value in parsed_args.items():
if arg in converters:
value = converters[arg](value)
kwargs[arg] = value
# Use internal methods without passing redundant arguments
kwargs = _get_parsed_arguments()

if step.datatable is not None:
if "datatable" in func_sig.parameters and step.datatable is not None:
kwargs["datatable"] = step.datatable.raw()

if step.docstring is not None:
if "docstring" in func_sig.parameters and step.docstring is not None:
kwargs["docstring"] = step.docstring

for arg in args:
if arg not in kwargs:
kwargs[arg] = request.getfixturevalue(arg)
kwargs = _get_argument_values(kwargs)

kw["step_func_args"] = kwargs

request.config.hook.pytest_bdd_before_step_call(**kw)
# 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=context.step_func, request=request, kwargs=kwargs)

except Exception as exception:
request.config.hook.pytest_bdd_step_error(exception=exception, **kw)
raise
Expand Down

0 comments on commit a9ce43f

Please sign in to comment.