Removing clutter from the step_func
This commit is contained in:
parent
533b5cd2f0
commit
6e1566bb68
|
@ -22,7 +22,7 @@ from _pytest.fixtures import FixtureLookupError, FixtureManager, FixtureRequest,
|
|||
|
||||
from . import exceptions
|
||||
from .feature import get_feature, get_features
|
||||
from .steps import get_step_fixture_name, inject_fixture
|
||||
from .steps import StepFuncContext, get_step_fixture_name, inject_fixture
|
||||
from .utils import CONFIG_STACK, get_args, get_caller_module_locals, get_caller_module_path
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -43,23 +43,22 @@ def find_argumented_step_fixture_name(
|
|||
# happens to be that _arg2fixturedefs is changed during the iteration so we use a copy
|
||||
for fixturename, fixturedefs in list(fixturemanager._arg2fixturedefs.items()):
|
||||
for fixturedef in fixturedefs:
|
||||
parsers = getattr(fixturedef.func, "_pytest_bdd_parsers", [])
|
||||
step_func = getattr(fixturedef.func, "_pytest_bdd_wrapped_step_func", None)
|
||||
if step_func is None:
|
||||
continue
|
||||
# TODO: See if we can avoid this:
|
||||
parsers = getattr(step_func, "_pytest_bdd_parsers", [])
|
||||
for parser in parsers:
|
||||
match = parser.is_matching(name)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
parser_name = get_step_fixture_name(parser.name, type_)
|
||||
if request:
|
||||
try:
|
||||
request.getfixturevalue(parser_name)
|
||||
except FixtureLookupError:
|
||||
continue
|
||||
return parser_name
|
||||
return None
|
||||
|
||||
|
||||
def _find_step_function(request: FixtureRequest, step: Step, scenario: Scenario) -> Any:
|
||||
def _find_step_function(request: FixtureRequest, step: Step, scenario: Scenario) -> StepFuncContext:
|
||||
"""Match the step defined by the regular expression pattern.
|
||||
|
||||
:param request: PyTest request object.
|
||||
|
@ -87,7 +86,9 @@ def _find_step_function(request: FixtureRequest, step: Step, scenario: Scenario)
|
|||
) from e2
|
||||
|
||||
|
||||
def _execute_step_function(request: FixtureRequest, scenario: Scenario, step: Step, step_func: Callable) -> None:
|
||||
def _execute_step_function(
|
||||
request: FixtureRequest, scenario: Scenario, step: Step, step_func_context: StepFuncContext
|
||||
) -> None:
|
||||
"""Execute step function.
|
||||
|
||||
:param request: PyTest request.
|
||||
|
@ -96,28 +97,25 @@ def _execute_step_function(request: FixtureRequest, scenario: Scenario, step: St
|
|||
:param function step_func: Step function.
|
||||
:param example: Example table.
|
||||
"""
|
||||
step_func = step_func_context.func
|
||||
|
||||
kw = dict(request=request, feature=scenario.feature, scenario=scenario, step=step, step_func=step_func)
|
||||
|
||||
request.config.hook.pytest_bdd_before_step(**kw)
|
||||
|
||||
kw["step_func_args"] = {}
|
||||
try:
|
||||
# Get the step argument values.
|
||||
converters = getattr(step_func, "converters", {})
|
||||
kwargs = {}
|
||||
|
||||
parsers = getattr(step_func, "_pytest_bdd_parsers", [])
|
||||
parser = step_func_context.parser
|
||||
for arg, value in parser.parse_arguments(step.name).items():
|
||||
if arg in converters:
|
||||
value = converters[arg](value)
|
||||
kwargs[arg] = value
|
||||
|
||||
for parser in parsers:
|
||||
if not parser.is_matching(step.name):
|
||||
continue
|
||||
for arg, value in parser.parse_arguments(step.name).items():
|
||||
if arg in converters:
|
||||
value = converters[arg](value)
|
||||
kwargs[arg] = value
|
||||
break
|
||||
|
||||
kwargs = {arg: kwargs[arg] if arg in kwargs else request.getfixturevalue(arg) for arg in get_args(step_func)}
|
||||
args = get_args(step_func)
|
||||
kwargs = {arg: kwargs[arg] if arg in kwargs else request.getfixturevalue(arg) for arg in args}
|
||||
kw["step_func_args"] = kwargs
|
||||
|
||||
request.config.hook.pytest_bdd_before_step_call(**kw)
|
||||
|
|
|
@ -37,11 +37,12 @@ def given_beautiful_article(article):
|
|||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
from dataclasses import dataclass
|
||||
|
||||
import pytest
|
||||
from _pytest.fixtures import FixtureDef, FixtureRequest
|
||||
|
||||
from .parsers import get_parser
|
||||
from .parsers import StepParser, get_parser
|
||||
from .types import GIVEN, THEN, WHEN
|
||||
from .utils import get_caller_module_locals, setdefault
|
||||
|
||||
|
@ -103,6 +104,12 @@ def then(name: Any, converters: dict[str, Callable] | None = None, target_fixtur
|
|||
return _step_decorator(THEN, name, converters=converters, target_fixture=target_fixture)
|
||||
|
||||
|
||||
@dataclass
|
||||
class StepFuncContext:
|
||||
func: Callable[..., Any]
|
||||
parser: StepParser
|
||||
|
||||
|
||||
def _step_decorator(
|
||||
step_type: str,
|
||||
step_name: Any,
|
||||
|
@ -126,19 +133,12 @@ def _step_decorator(
|
|||
|
||||
# TODO: Try to not attach to both step_func and lazy_step_func
|
||||
|
||||
step_func.__name__ = str(parsed_step_name)
|
||||
def lazy_step_func() -> StepFuncContext:
|
||||
return StepFuncContext(func=step_func, parser=parser_instance)
|
||||
|
||||
def lazy_step_func() -> Callable:
|
||||
return step_func
|
||||
|
||||
step_func.step_type = step_type
|
||||
lazy_step_func.step_type = step_type
|
||||
|
||||
# Preserve the docstring
|
||||
lazy_step_func.__doc__ = func.__doc__
|
||||
lazy_step_func._pytest_bdd_wrapped_step_func = step_func
|
||||
|
||||
setdefault(step_func, "_pytest_bdd_parsers", []).append(parser_instance)
|
||||
setdefault(lazy_step_func, "_pytest_bdd_parsers", []).append(parser_instance)
|
||||
|
||||
if converters:
|
||||
step_func.converters = lazy_step_func.converters = converters
|
||||
|
|
|
@ -219,13 +219,13 @@ def test_local(testdir):
|
|||
|
||||
fixture = request.getfixturevalue(
|
||||
get_step_fixture_name("I have a parent fixture", GIVEN)
|
||||
)
|
||||
).func
|
||||
assert fixture() == "local"
|
||||
|
||||
|
||||
fixture = request.getfixturevalue(
|
||||
get_step_fixture_name("I have an overridable fixture", GIVEN)
|
||||
)
|
||||
).func
|
||||
assert fixture() == "local"
|
||||
|
||||
"""
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_when_then(testdir):
|
||||
"""Test when and then steps are callable functions.
|
||||
|
@ -30,42 +28,13 @@ def test_when_then(testdir):
|
|||
|
||||
def test_when_then(request):
|
||||
do_stuff_ = request.getfixturevalue(get_step_fixture_name("I do stuff", WHEN))
|
||||
assert callable(do_stuff_)
|
||||
assert callable(do_stuff_.func)
|
||||
|
||||
check_stuff_ = request.getfixturevalue(get_step_fixture_name("I check stuff", THEN))
|
||||
assert callable(check_stuff_)
|
||||
assert callable(check_stuff_.func)
|
||||
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.assert_outcomes(passed=1)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("step", "keyword"),
|
||||
[("given", "Given"), ("when", "When"), ("then", "Then")],
|
||||
)
|
||||
def test_preserve_decorator(testdir, step, keyword):
|
||||
"""Check that we preserve original function attributes after decorating it."""
|
||||
testdir.makepyfile(
|
||||
textwrap.dedent(
|
||||
'''\
|
||||
from pytest_bdd import {step}
|
||||
from pytest_bdd.steps import get_step_fixture_name
|
||||
|
||||
@{step}("{keyword}")
|
||||
def func():
|
||||
"""Doc string."""
|
||||
|
||||
def test_decorator():
|
||||
assert globals()[get_step_fixture_name("{keyword}", {step}.__name__)].__doc__ == "Doc string."
|
||||
|
||||
|
||||
'''.format(
|
||||
step=step, keyword=keyword
|
||||
)
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.assert_outcomes(passed=1)
|
||||
|
|
Loading…
Reference in New Issue