This commit is contained in:
Alessio Bogon 2022-07-27 20:06:20 +02:00
parent b316d68232
commit 2d64e9f135
3 changed files with 32 additions and 23 deletions

View File

@ -10,7 +10,7 @@ from mako.lookup import TemplateLookup
from .feature import get_features
from .scenario import make_python_docstring, make_python_name, make_string_literal, patch_argumented_step_functions
from .steps import get_parsed_step_fixture_name
from .steps import get_step_fixture_name
from .types import STEP_TYPES
if TYPE_CHECKING:
@ -128,7 +128,7 @@ def _find_step_fixturedef(
) -> Sequence[FixtureDef[Any]] | None:
"""Find step fixturedef."""
with patch_argumented_step_functions(name=name, type_=type_, fixturemanager=fixturemanager, nodeid=item.nodeid):
bdd_name = get_parsed_step_fixture_name(name, type_)
bdd_name = get_step_fixture_name(name, type_)
return fixturemanager.getfixturedefs(bdd_name, item.nodeid)

View File

@ -24,7 +24,7 @@ from _pytest.nodes import iterparentnodeids
from . import exceptions
from .feature import get_feature, get_features
from .steps import StepFunctionContext, get_parsed_step_fixture_name, inject_fixture
from .steps import StepFunctionContext, 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,7 +43,7 @@ ALPHA_REGEX = re.compile(r"^\d+_*")
def iter_argumented_step_function(
name: str, type_: str, fixturemanager: FixtureManager, nodeid: str | None = None
name: str, type_: str, fixturemanager: FixtureManager, nodeid: str
) -> Iterable[FixtureDef[Any]]:
"""Iterate over argumented step functions."""
# happens to be that _arg2fixturedefs is changed during the iteration so we use a copy
@ -61,35 +61,35 @@ def iter_argumented_step_function(
if not match:
continue
if nodeid is not None:
if fixturedef not in fixturemanager.getfixturedefs(fixturename, nodeid):
continue
if fixturedef not in fixturemanager.getfixturedefs(fixturename, nodeid):
continue
yield fixturedef
@contextlib.contextmanager
def patch_argumented_step_functions(name: str, type_, fixturemanager, nodeid: str | None = None) -> None:
bdd_name = get_parsed_step_fixture_name(name, type_)
bdd_name = get_step_fixture_name(name, type_)
fixturedefs = list(
iter_argumented_step_function(name=name, type_=type_, fixturemanager=fixturemanager, nodeid=nodeid)
)
# Sort the fixture definitions by their "path", so that the "pytestbdd_parsed_" fixturedef will
# Sort the fixture definitions by their "path", so that the `bdd_name` fixture will
# respect the fixture scope
fixture_defs_by_path = [(tuple(iterparentnodeids(x.baseid)), x) for x in fixturedefs]
resorted = sorted(fixture_defs_by_path, key=lambda x: x[0])
if not resorted:
def get_fixture_path(fixture_def: FixtureDef) -> list[str]:
return list(iterparentnodeids(fixture_def.baseid))
fixturedefs.sort(key=lambda x: get_fixture_path(x))
if not fixturedefs:
yield
return
bdd_step_defs = fixturemanager._arg2fixturedefs[bdd_name] = []
for fixture_path, fixturedef in resorted:
if fixturedef not in bdd_step_defs:
logger.debug("Adding provider for fixture %r}: %s", bdd_name, fixturedef)
bdd_step_defs.append(fixturedef)
else:
logger.warning("%r already added to bdd name %r, SKIPPING", fixturedef, bdd_name)
logger.debug("Adding providers for fixture %r: %r", bdd_name, fixturedefs)
fixturemanager._arg2fixturedefs[bdd_name] = fixturedefs
try:
yield
finally:
@ -98,7 +98,7 @@ def patch_argumented_step_functions(name: str, type_, fixturemanager, nodeid: st
def get_argumented_step_function(request, name: str, type_: str) -> StepFunctionContext | None:
"""Find argumented step fixture name."""
bdd_name = get_parsed_step_fixture_name(name, type_)
bdd_name = get_step_fixture_name(name, type_)
try:
return request.getfixturevalue(bdd_name)
except pytest.FixtureLookupError:

View File

@ -36,6 +36,7 @@ def _(article):
"""
from __future__ import annotations
import enum
from dataclasses import dataclass, field
from itertools import count
from typing import Any, Callable, Iterable, TypeVar
@ -51,6 +52,12 @@ from .utils import get_caller_module_locals
TCallable = TypeVar("TCallable", bound=Callable[..., Any])
@enum.unique
class StepNamePrefix(enum.Enum):
step_def = "pytestbdd_stepdef"
step_impl = "pytestbdd_stepimpl"
@dataclass
class StepFunctionContext:
type: Literal["given", "when", "then"]
@ -60,7 +67,7 @@ class StepFunctionContext:
target_fixture: str | None = None
def get_parsed_step_fixture_name(name: str, type_: str) -> str:
def get_step_fixture_name(name: str, type_: str) -> str:
"""Get step fixture name.
:param name: string
@ -68,7 +75,7 @@ def get_parsed_step_fixture_name(name: str, type_: str) -> str:
:return: step fixture name
:rtype: string
"""
return f"pytestbdd_parsed_{type_}_{name}"
return f"{StepNamePrefix.step_impl}_{type_}_{name}"
def given(
@ -170,7 +177,9 @@ def _step_decorator(
step_function_marker._pytest_bdd_step_context = context
caller_locals = get_caller_module_locals()
fixture_step_name = find_unique_name(f"pytestbdd_stepdef_{step_type}_{parser.name}", seen=caller_locals.keys())
fixture_step_name = find_unique_name(
f"{StepNamePrefix.step_def}_{step_type}_{parser.name}", seen=caller_locals.keys()
)
caller_locals[fixture_step_name] = pytest.fixture(name=fixture_step_name)(step_function_marker)
return func