First commit for setting default parser
This commit is contained in:
parent
c4f44dc3b3
commit
e41d8b9a6f
|
@ -17,12 +17,18 @@ if TYPE_CHECKING:
|
|||
def add_options(parser: PytestArgParser) -> None:
|
||||
"""Add pytest-bdd options."""
|
||||
group = parser.getgroup("bdd")
|
||||
group.addoption(
|
||||
group._addoption(
|
||||
"--bdd-default-parser",
|
||||
dest="bdd_default_parser",
|
||||
action="store",
|
||||
default=None,
|
||||
help="Set the default step parser type (e.g. string, parse, re, cfparse).",
|
||||
)
|
||||
parser._addini(
|
||||
"bdd_default_parser",
|
||||
help="Default step parser type (e.g. string, parse, re, cfparse) for pytest-bdd.",
|
||||
default=None,
|
||||
)
|
||||
|
||||
|
||||
def configure(config: Config):
|
||||
|
@ -129,19 +135,17 @@ class string(StepParser):
|
|||
TStepParser = TypeVar("TStepParser", bound=StepParser)
|
||||
|
||||
|
||||
def get_parser(step_name: str | StepParser, config: Config | None = None) -> StepParser:
|
||||
"""Get parser by given name."""
|
||||
def get_parser(step_name: str | StepParser, config: Config) -> StepParser:
|
||||
if isinstance(step_name, StepParser):
|
||||
return step_name
|
||||
|
||||
default_parser = getattr(config, "_bdd_default_parser", "string") if config else "string"
|
||||
|
||||
parser_classes = {
|
||||
"string": string,
|
||||
"parse": parse,
|
||||
"re": re,
|
||||
"cfparse": cfparse,
|
||||
}
|
||||
|
||||
parser_cls = parser_classes.get(default_parser, string)
|
||||
return parser_cls(step_name)
|
||||
if config:
|
||||
default_parser = getattr(config, "_bdd_default_parser", "string")
|
||||
parser_classes = {
|
||||
"string": string,
|
||||
"parse": parse,
|
||||
"re": re,
|
||||
"cfparse": cfparse,
|
||||
}
|
||||
parser_cls = parser_classes.get(default_parser, string)
|
||||
return parser_cls(step_name)
|
||||
return string(step_name)
|
||||
|
|
|
@ -24,7 +24,7 @@ import pytest
|
|||
from _pytest.fixtures import FixtureDef, FixtureManager, FixtureRequest, call_fixture_func
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
from . import exceptions
|
||||
from . import exceptions, steps
|
||||
from .compat import getfixturedefs, inject_fixture
|
||||
from .feature import get_feature, get_features
|
||||
from .steps import StepFunctionContext, get_step_fixture_name
|
||||
|
@ -52,13 +52,14 @@ def find_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, node:
|
|||
fixture_def_by_name = list(fixturemanager._arg2fixturedefs.items())
|
||||
for fixturename, fixturedefs in fixture_def_by_name:
|
||||
for _, fixturedef in enumerate(fixturedefs):
|
||||
step_func_context = getattr(fixturedef.func, "_pytest_bdd_step_context", None)
|
||||
step_func_context: steps.StepFunctionContext = getattr(fixturedef.func, "_pytest_bdd_step_context", None)
|
||||
if step_func_context is None:
|
||||
continue
|
||||
|
||||
if step_func_context.type is not None and step_func_context.type != step.type:
|
||||
continue
|
||||
|
||||
steps.register_step_context(step_func_context, node.config)
|
||||
match = step_func_context.parser.is_matching(step.name)
|
||||
if not match:
|
||||
continue
|
||||
|
|
|
@ -41,7 +41,7 @@ import enum
|
|||
from collections.abc import Iterable
|
||||
from dataclasses import dataclass, field
|
||||
from itertools import count
|
||||
from typing import Any, Callable, Literal, TypeVar
|
||||
from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar
|
||||
|
||||
import pytest
|
||||
from typing_extensions import ParamSpec
|
||||
|
@ -51,6 +51,10 @@ from .parsers import StepParser, get_parser
|
|||
from .types import GIVEN, THEN, WHEN
|
||||
from .utils import get_caller_module_locals
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _pytest.config import Config
|
||||
|
||||
|
||||
P = ParamSpec("P")
|
||||
T = TypeVar("T")
|
||||
|
||||
|
@ -63,11 +67,12 @@ class StepNamePrefix(enum.Enum):
|
|||
|
||||
@dataclass
|
||||
class StepFunctionContext:
|
||||
name: str
|
||||
type: Literal["given", "when", "then"] | None
|
||||
step_func: Callable[..., Any]
|
||||
parser: StepParser
|
||||
converters: dict[str, Callable[[str], Any]] = field(default_factory=dict)
|
||||
target_fixture: str | None = None
|
||||
parser: StepParser | None = None
|
||||
|
||||
|
||||
def get_step_fixture_name(step: Step) -> str:
|
||||
|
@ -159,12 +164,10 @@ def step(
|
|||
converters = {}
|
||||
|
||||
def decorator(func: Callable[P, T]) -> Callable[P, T]:
|
||||
parser = get_parser(name)
|
||||
|
||||
context = StepFunctionContext(
|
||||
name=name,
|
||||
type=type_,
|
||||
step_func=func,
|
||||
parser=parser,
|
||||
converters=converters,
|
||||
target_fixture=target_fixture,
|
||||
)
|
||||
|
@ -175,8 +178,9 @@ def step(
|
|||
step_function_marker._pytest_bdd_step_context = context # type: ignore
|
||||
|
||||
caller_locals = get_caller_module_locals(stacklevel=stacklevel)
|
||||
step_name = name.name if isinstance(name, StepParser) else name
|
||||
fixture_step_name = find_unique_name(
|
||||
f"{StepNamePrefix.step_def.value}_{type_ or '*'}_{parser.name}", seen=caller_locals.keys()
|
||||
f"{StepNamePrefix.step_def.value}_{type_ or '*'}_{step_name}", seen=caller_locals.keys()
|
||||
)
|
||||
caller_locals[fixture_step_name] = pytest.fixture(name=fixture_step_name)(step_function_marker)
|
||||
return func
|
||||
|
@ -205,3 +209,9 @@ def find_unique_name(name: str, seen: Iterable[str]) -> str:
|
|||
|
||||
# This line will never be reached, but it's here to satisfy mypy
|
||||
raise RuntimeError("Unable to find a unique name")
|
||||
|
||||
|
||||
def register_step_context(step_context: StepFunctionContext, config: Config):
|
||||
"""Ensure step context has a parser set early in the lifecycle."""
|
||||
if step_context.parser is None:
|
||||
step_context.parser = get_parser(step_context.name, config)
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import textwrap
|
||||
|
||||
|
||||
def test_tags_selector(pytester):
|
||||
"""Test tests selection by tags."""
|
||||
pytester.makefile(
|
||||
".ini",
|
||||
pytest=textwrap.dedent(
|
||||
"""
|
||||
[pytest]
|
||||
bdd_default_parser = string
|
||||
"""
|
||||
),
|
||||
)
|
||||
pytester.makefile(
|
||||
".feature",
|
||||
parser=textwrap.dedent(
|
||||
"""\
|
||||
Feature: Step arguments
|
||||
Scenario: Every step takes a parameter with the same name
|
||||
Given I have 1 Euro
|
||||
When I pay 2 Euro
|
||||
And I pay 1 Euro
|
||||
Then I should have 0 Euro
|
||||
And I should have 999999 Euro
|
||||
|
||||
"""
|
||||
),
|
||||
)
|
||||
|
||||
pytester.makepyfile(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
from pytest_bdd import parsers, given, when, then, scenarios
|
||||
|
||||
scenarios("parser.feature")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def values():
|
||||
return [1, 2, 1, 0, 999999]
|
||||
|
||||
|
||||
@given("I have {euro:d} Euro")
|
||||
def _(euro, values):
|
||||
assert euro == values.pop(0)
|
||||
|
||||
|
||||
@when("I pay {euro:d} Euro")
|
||||
def _(euro, values, request):
|
||||
assert euro == values.pop(0)
|
||||
|
||||
|
||||
@then("I should have {euro:d} Euro")
|
||||
def _(euro, values):
|
||||
assert euro == values.pop(0)
|
||||
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = pytester.runpytest()
|
||||
result.assert_outcomes(passed=1)
|
Loading…
Reference in New Issue