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 . import exceptions
|
||||||
from .feature import get_feature, get_features
|
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
|
from .utils import CONFIG_STACK, get_args, get_caller_module_locals, get_caller_module_path
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
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
|
# happens to be that _arg2fixturedefs is changed during the iteration so we use a copy
|
||||||
for fixturename, fixturedefs in list(fixturemanager._arg2fixturedefs.items()):
|
for fixturename, fixturedefs in list(fixturemanager._arg2fixturedefs.items()):
|
||||||
for fixturedef in fixturedefs:
|
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:
|
for parser in parsers:
|
||||||
match = parser.is_matching(name)
|
match = parser.is_matching(name)
|
||||||
if not match:
|
if not match:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
parser_name = get_step_fixture_name(parser.name, type_)
|
parser_name = get_step_fixture_name(parser.name, type_)
|
||||||
if request:
|
|
||||||
try:
|
|
||||||
request.getfixturevalue(parser_name)
|
|
||||||
except FixtureLookupError:
|
|
||||||
continue
|
|
||||||
return parser_name
|
return parser_name
|
||||||
return None
|
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.
|
"""Match the step defined by the regular expression pattern.
|
||||||
|
|
||||||
:param request: PyTest request object.
|
:param request: PyTest request object.
|
||||||
|
@ -87,7 +86,9 @@ def _find_step_function(request: FixtureRequest, step: Step, scenario: Scenario)
|
||||||
) from e2
|
) 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.
|
"""Execute step function.
|
||||||
|
|
||||||
:param request: PyTest request.
|
: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 function step_func: Step function.
|
||||||
:param example: Example table.
|
: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)
|
kw = dict(request=request, feature=scenario.feature, scenario=scenario, step=step, step_func=step_func)
|
||||||
|
|
||||||
request.config.hook.pytest_bdd_before_step(**kw)
|
request.config.hook.pytest_bdd_before_step(**kw)
|
||||||
|
|
||||||
kw["step_func_args"] = {}
|
kw["step_func_args"] = {}
|
||||||
try:
|
try:
|
||||||
# Get the step argument values.
|
# Get the step argument values.
|
||||||
converters = getattr(step_func, "converters", {})
|
converters = getattr(step_func, "converters", {})
|
||||||
kwargs = {}
|
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:
|
args = get_args(step_func)
|
||||||
if not parser.is_matching(step.name):
|
kwargs = {arg: kwargs[arg] if arg in kwargs else request.getfixturevalue(arg) for arg in args}
|
||||||
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)}
|
|
||||||
kw["step_func_args"] = kwargs
|
kw["step_func_args"] = kwargs
|
||||||
|
|
||||||
request.config.hook.pytest_bdd_before_step_call(**kw)
|
request.config.hook.pytest_bdd_before_step_call(**kw)
|
||||||
|
|
|
@ -37,11 +37,12 @@ def given_beautiful_article(article):
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import typing
|
import typing
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.fixtures import FixtureDef, FixtureRequest
|
from _pytest.fixtures import FixtureDef, FixtureRequest
|
||||||
|
|
||||||
from .parsers import get_parser
|
from .parsers import StepParser, get_parser
|
||||||
from .types import GIVEN, THEN, WHEN
|
from .types import GIVEN, THEN, WHEN
|
||||||
from .utils import get_caller_module_locals, setdefault
|
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)
|
return _step_decorator(THEN, name, converters=converters, target_fixture=target_fixture)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StepFuncContext:
|
||||||
|
func: Callable[..., Any]
|
||||||
|
parser: StepParser
|
||||||
|
|
||||||
|
|
||||||
def _step_decorator(
|
def _step_decorator(
|
||||||
step_type: str,
|
step_type: str,
|
||||||
step_name: Any,
|
step_name: Any,
|
||||||
|
@ -126,19 +133,12 @@ def _step_decorator(
|
||||||
|
|
||||||
# TODO: Try to not attach to both step_func and lazy_step_func
|
# 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:
|
lazy_step_func._pytest_bdd_wrapped_step_func = step_func
|
||||||
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__
|
|
||||||
|
|
||||||
setdefault(step_func, "_pytest_bdd_parsers", []).append(parser_instance)
|
setdefault(step_func, "_pytest_bdd_parsers", []).append(parser_instance)
|
||||||
setdefault(lazy_step_func, "_pytest_bdd_parsers", []).append(parser_instance)
|
|
||||||
|
|
||||||
if converters:
|
if converters:
|
||||||
step_func.converters = lazy_step_func.converters = converters
|
step_func.converters = lazy_step_func.converters = converters
|
||||||
|
|
|
@ -219,13 +219,13 @@ def test_local(testdir):
|
||||||
|
|
||||||
fixture = request.getfixturevalue(
|
fixture = request.getfixturevalue(
|
||||||
get_step_fixture_name("I have a parent fixture", GIVEN)
|
get_step_fixture_name("I have a parent fixture", GIVEN)
|
||||||
)
|
).func
|
||||||
assert fixture() == "local"
|
assert fixture() == "local"
|
||||||
|
|
||||||
|
|
||||||
fixture = request.getfixturevalue(
|
fixture = request.getfixturevalue(
|
||||||
get_step_fixture_name("I have an overridable fixture", GIVEN)
|
get_step_fixture_name("I have an overridable fixture", GIVEN)
|
||||||
)
|
).func
|
||||||
assert fixture() == "local"
|
assert fixture() == "local"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
def test_when_then(testdir):
|
def test_when_then(testdir):
|
||||||
"""Test when and then steps are callable functions.
|
"""Test when and then steps are callable functions.
|
||||||
|
@ -30,42 +28,13 @@ def test_when_then(testdir):
|
||||||
|
|
||||||
def test_when_then(request):
|
def test_when_then(request):
|
||||||
do_stuff_ = request.getfixturevalue(get_step_fixture_name("I do stuff", WHEN))
|
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))
|
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 = testdir.runpytest()
|
||||||
result.assert_outcomes(passed=1)
|
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