Revert "Allows use <params> in parsers defined steps"
This reverts commit 7850f218b7
.
This commit is contained in:
parent
4f1a0f8707
commit
f75a57b22b
|
@ -1,3 +1,4 @@
|
|||
import io
|
||||
import os.path
|
||||
import re
|
||||
import textwrap
|
||||
|
@ -456,5 +457,4 @@ def get_tags(line):
|
|||
return {tag.lstrip("@") for tag in line.strip().split(" @") if len(tag) > 1}
|
||||
|
||||
|
||||
STEP_PARAM_TEMPLATE = "<{param}>"
|
||||
STEP_PARAM_RE = re.compile(STEP_PARAM_TEMPLATE.format(param="((?<=<)[^<>]+(?=>))"))
|
||||
STEP_PARAM_RE = re.compile(r"\<(.+?)\>")
|
||||
|
|
|
@ -10,16 +10,15 @@ test_publish_article = scenario(
|
|||
scenario_name="Publishing the article",
|
||||
)
|
||||
"""
|
||||
import collections
|
||||
import os
|
||||
import re
|
||||
from itertools import chain, product
|
||||
|
||||
import pytest
|
||||
from _pytest.fixtures import FixtureLookupError
|
||||
|
||||
from . import exceptions
|
||||
from .feature import get_feature, get_features
|
||||
from .parser import STEP_PARAM_RE, STEP_PARAM_TEMPLATE
|
||||
from .steps import get_step_fixture_name, inject_fixture
|
||||
from .utils import CONFIG_STACK, get_args, get_caller_module_locals, get_caller_module_path
|
||||
|
||||
|
@ -27,61 +26,31 @@ PYTHON_REPLACE_REGEX = re.compile(r"\W")
|
|||
ALPHA_REGEX = re.compile(r"^\d+_*")
|
||||
|
||||
|
||||
def generate_partial_substituted_step_parameters(name: str, request):
|
||||
"""\
|
||||
Returns step name with substituted parameters from fixtures, example tables, param marks starting
|
||||
from most substituted to less, so giving chance to most specific parser
|
||||
"""
|
||||
matches = re.finditer(STEP_PARAM_RE, name)
|
||||
for match in matches:
|
||||
param_name = match.group(1)
|
||||
try:
|
||||
sub_name = re.sub(
|
||||
STEP_PARAM_TEMPLATE.format(param=re.escape(param_name)),
|
||||
str(request.getfixturevalue(param_name)),
|
||||
name,
|
||||
count=1,
|
||||
)
|
||||
except FixtureLookupError:
|
||||
continue
|
||||
else:
|
||||
yield from generate_partial_substituted_step_parameters(sub_name, request)
|
||||
yield name
|
||||
|
||||
|
||||
def find_argumented_step_fixture_name(name, type_, fixturemanager, request=None):
|
||||
"""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:
|
||||
parser = getattr(fixturedef.func, "parser", None)
|
||||
if parser is None:
|
||||
continue
|
||||
match = parser.is_matching(name)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
fixturedefs = chain.from_iterable(fixturedefs for _, fixturedefs in list(fixturemanager._arg2fixturedefs.items()))
|
||||
fixturedef_funcs = (fixturedef.func for fixturedef in fixturedefs)
|
||||
parsers_fixturedef_function_mappings = (
|
||||
(fixturedef_func.parser, fixturedef_func)
|
||||
for fixturedef_func in fixturedef_funcs
|
||||
if hasattr(fixturedef_func, "parser")
|
||||
)
|
||||
|
||||
matched_steps_with_parsers = (
|
||||
(step_name, parser, getattr(fixturedef_function, "converters", {}))
|
||||
for step_name, (parser, fixturedef_function) in product(
|
||||
generate_partial_substituted_step_parameters(name, request), parsers_fixturedef_function_mappings
|
||||
)
|
||||
if parser.is_matching(step_name)
|
||||
)
|
||||
|
||||
for step_name, parser, converters in matched_steps_with_parsers:
|
||||
if request:
|
||||
for arg, value in parser.parse_arguments(step_name).items():
|
||||
converters = getattr(fixturedef.func, "converters", {})
|
||||
for arg, value in parser.parse_arguments(name).items():
|
||||
if arg in converters:
|
||||
value = converters[arg](value)
|
||||
try:
|
||||
overridable_fixture_value = request.getfixturevalue(arg)
|
||||
except FixtureLookupError:
|
||||
if request:
|
||||
inject_fixture(request, arg, value)
|
||||
else:
|
||||
if overridable_fixture_value != value:
|
||||
inject_fixture(request, arg, value)
|
||||
return 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
|
||||
|
||||
|
||||
def _find_step_function(request, step, scenario):
|
||||
|
@ -169,6 +138,9 @@ def _execute_scenario(feature, scenario, request):
|
|||
request.config.hook.pytest_bdd_after_scenario(request=request, feature=feature, scenario=scenario)
|
||||
|
||||
|
||||
FakeRequest = collections.namedtuple("FakeRequest", ["module"])
|
||||
|
||||
|
||||
def _get_scenario_decorator(feature, feature_name, scenario, scenario_name):
|
||||
# HACK: Ideally we would use `def decorator(fn)`, but we want to return a custom exception
|
||||
# when the decorator is misused.
|
||||
|
|
|
@ -1,28 +1,25 @@
|
|||
"""Scenario Outline tests."""
|
||||
import textwrap
|
||||
|
||||
from pytest import mark
|
||||
|
||||
from tests.utils import assert_outcomes
|
||||
|
||||
FLOAT_NUMBER_PATTERN = r"[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?"
|
||||
STEPS_TEMPLATE = """\
|
||||
STEPS = """\
|
||||
from pytest_bdd import given, when, then
|
||||
from pytest_bdd.parsers import re
|
||||
|
||||
{given_decorator_definition}
|
||||
|
||||
@given("there are <start> cucumbers", target_fixture="start_cucumbers")
|
||||
def start_cucumbers(start):
|
||||
assert isinstance(start, int)
|
||||
return dict(start=start)
|
||||
|
||||
|
||||
{when_decorator_definition}
|
||||
@when("I eat <eat> cucumbers")
|
||||
def eat_cucumbers(start_cucumbers, eat):
|
||||
assert isinstance(eat, float)
|
||||
start_cucumbers["eat"] = eat
|
||||
|
||||
|
||||
{then_decorator_definition}
|
||||
@then("I should have <left> cucumbers")
|
||||
def should_have_left_cucumbers(start_cucumbers, start, eat, left):
|
||||
assert isinstance(left, str)
|
||||
assert start - eat == int(left)
|
||||
|
@ -32,31 +29,7 @@ def should_have_left_cucumbers(start_cucumbers, start, eat, left):
|
|||
"""
|
||||
|
||||
|
||||
STRING_STEPS = STEPS_TEMPLATE.format(
|
||||
given_decorator_definition='@given("there are <start> cucumbers", target_fixture="start_cucumbers")',
|
||||
when_decorator_definition='@when("I eat <eat> cucumbers")',
|
||||
then_decorator_definition='@then("I should have <left> cucumbers")',
|
||||
)
|
||||
|
||||
PARSER_STEPS = STEPS_TEMPLATE.format(
|
||||
given_decorator_definition=f'@given(re("there are (?P<start>{FLOAT_NUMBER_PATTERN}) cucumbers"), '
|
||||
f'target_fixture="start_cucumbers")',
|
||||
when_decorator_definition=f'@when(re("I eat (?P<eat>{FLOAT_NUMBER_PATTERN}) cucumbers"))',
|
||||
then_decorator_definition=f'@then(re("I should have (?P<left>{FLOAT_NUMBER_PATTERN}) cucumbers"))',
|
||||
)
|
||||
|
||||
PARSER_STEPS_CONVERTED = STEPS_TEMPLATE.format(
|
||||
given_decorator_definition=f'@given(re("there are (?P<start>{FLOAT_NUMBER_PATTERN}) cucumbers"), '
|
||||
f'target_fixture="start_cucumbers", converters=dict(start=int))',
|
||||
when_decorator_definition=f'@when(re("I eat (?P<eat>{FLOAT_NUMBER_PATTERN}) cucumbers"), '
|
||||
f"converters=dict(eat=float))",
|
||||
then_decorator_definition=f'@then(re("I should have (?P<left>{FLOAT_NUMBER_PATTERN}) cucumbers"), '
|
||||
f"converters=dict(left=str))",
|
||||
)
|
||||
|
||||
|
||||
@mark.parametrize("steps", [STRING_STEPS, PARSER_STEPS_CONVERTED])
|
||||
def test_outlined(testdir, steps):
|
||||
def test_outlined(testdir):
|
||||
testdir.makefile(
|
||||
".feature",
|
||||
outline=textwrap.dedent(
|
||||
|
@ -76,7 +49,7 @@ def test_outlined(testdir, steps):
|
|||
),
|
||||
)
|
||||
|
||||
testdir.makeconftest(textwrap.dedent(steps))
|
||||
testdir.makeconftest(textwrap.dedent(STEPS))
|
||||
|
||||
testdir.makepyfile(
|
||||
textwrap.dedent(
|
||||
|
@ -105,9 +78,8 @@ def test_outlined(testdir, steps):
|
|||
result.assert_outcomes(passed=2)
|
||||
|
||||
|
||||
@mark.parametrize("steps", [STRING_STEPS, PARSER_STEPS_CONVERTED])
|
||||
def test_outline_has_subset_of_parameters(testdir, steps):
|
||||
"""Test parametrized scenario when the test function has a subset of the parameters of the examples."""
|
||||
def test_wrongly_outlined(testdir):
|
||||
"""Test parametrized scenario when the test function lacks parameters."""
|
||||
|
||||
testdir.makefile(
|
||||
".feature",
|
||||
|
@ -126,7 +98,7 @@ def test_outline_has_subset_of_parameters(testdir, steps):
|
|||
"""
|
||||
),
|
||||
)
|
||||
testdir.makeconftest(textwrap.dedent(steps))
|
||||
testdir.makeconftest(textwrap.dedent(STEPS))
|
||||
|
||||
testdir.makepyfile(
|
||||
textwrap.dedent(
|
||||
|
@ -147,8 +119,7 @@ def test_outline_has_subset_of_parameters(testdir, steps):
|
|||
result.stdout.fnmatch_lines("*should match set of example values [[]'eat', 'left', 'start', 'unknown_param'[]].*")
|
||||
|
||||
|
||||
@mark.parametrize("steps", [STRING_STEPS, PARSER_STEPS])
|
||||
def test_wrong_vertical_examples_scenario(testdir, steps):
|
||||
def test_wrong_vertical_examples_scenario(testdir):
|
||||
"""Test parametrized scenario vertical example table has wrong format."""
|
||||
testdir.makefile(
|
||||
".feature",
|
||||
|
@ -167,7 +138,7 @@ def test_wrong_vertical_examples_scenario(testdir, steps):
|
|||
"""
|
||||
),
|
||||
)
|
||||
testdir.makeconftest(textwrap.dedent(steps))
|
||||
testdir.makeconftest(textwrap.dedent(STEPS))
|
||||
|
||||
testdir.makepyfile(
|
||||
textwrap.dedent(
|
||||
|
@ -188,8 +159,7 @@ def test_wrong_vertical_examples_scenario(testdir, steps):
|
|||
)
|
||||
|
||||
|
||||
@mark.parametrize("steps", [STRING_STEPS, PARSER_STEPS])
|
||||
def test_wrong_vertical_examples_feature(testdir, steps):
|
||||
def test_wrong_vertical_examples_feature(testdir):
|
||||
"""Test parametrized feature vertical example table has wrong format."""
|
||||
testdir.makefile(
|
||||
".feature",
|
||||
|
@ -209,7 +179,7 @@ def test_wrong_vertical_examples_feature(testdir, steps):
|
|||
"""
|
||||
),
|
||||
)
|
||||
testdir.makeconftest(textwrap.dedent(steps))
|
||||
testdir.makeconftest(textwrap.dedent(STEPS))
|
||||
|
||||
testdir.makepyfile(
|
||||
textwrap.dedent(
|
||||
|
@ -230,8 +200,7 @@ def test_wrong_vertical_examples_feature(testdir, steps):
|
|||
)
|
||||
|
||||
|
||||
@mark.parametrize("steps", [STRING_STEPS, PARSER_STEPS_CONVERTED])
|
||||
def test_outlined_with_other_fixtures(testdir, steps):
|
||||
def test_outlined_with_other_fixtures(testdir):
|
||||
"""Test outlined scenario also using other parametrized fixture."""
|
||||
testdir.makefile(
|
||||
".feature",
|
||||
|
@ -252,7 +221,7 @@ def test_outlined_with_other_fixtures(testdir, steps):
|
|||
),
|
||||
)
|
||||
|
||||
testdir.makeconftest(textwrap.dedent(steps))
|
||||
testdir.makeconftest(textwrap.dedent(STEPS))
|
||||
|
||||
testdir.makepyfile(
|
||||
textwrap.dedent(
|
||||
|
@ -282,8 +251,7 @@ def test_outlined_with_other_fixtures(testdir, steps):
|
|||
result.assert_outcomes(passed=6)
|
||||
|
||||
|
||||
@mark.parametrize("steps", [STRING_STEPS, PARSER_STEPS_CONVERTED])
|
||||
def test_vertical_example(testdir, steps):
|
||||
def test_vertical_example(testdir):
|
||||
"""Test outlined scenario with vertical examples table."""
|
||||
testdir.makefile(
|
||||
".feature",
|
||||
|
@ -304,7 +272,7 @@ def test_vertical_example(testdir, steps):
|
|||
),
|
||||
)
|
||||
|
||||
testdir.makeconftest(textwrap.dedent(steps))
|
||||
testdir.makeconftest(textwrap.dedent(STEPS))
|
||||
|
||||
testdir.makepyfile(
|
||||
textwrap.dedent(
|
||||
|
@ -333,62 +301,6 @@ def test_vertical_example(testdir, steps):
|
|||
result.assert_outcomes(passed=2)
|
||||
|
||||
|
||||
def test_outlined_paramaters_parsed_indirectly(testdir):
|
||||
testdir.makefile(
|
||||
".feature",
|
||||
outline=textwrap.dedent(
|
||||
"""\
|
||||
Feature: Outline
|
||||
|
||||
Examples:
|
||||
| first | consume | remaining |
|
||||
| 12 | 5 | 7 |
|
||||
| 5 | 4 | 1 |
|
||||
|
||||
Scenario Outline: Outlined modern given, when, thens
|
||||
Given there were <first> <foods>
|
||||
When I ate <consume> <foods>
|
||||
Then I should have had <remaining> <foods>
|
||||
|
||||
Examples:
|
||||
| foods |
|
||||
| ice-creams |
|
||||
| almonds |
|
||||
"""
|
||||
),
|
||||
)
|
||||
|
||||
testdir.makepyfile(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
from pytest_bdd import scenarios, given, when, then
|
||||
from pytest_bdd.parsers import parse
|
||||
|
||||
@given(parse('there were {start:d} {fruits}'), target_fixture='context')
|
||||
def started_fruits(start, fruits):
|
||||
assert isinstance(start, int)
|
||||
return {fruits: dict(start=start)}
|
||||
|
||||
@when(parse('I ate {eat:g} {fruits}'))
|
||||
def ate_fruits(start, eat, fruits, context):
|
||||
assert isinstance(eat, float)
|
||||
context[fruits]['eat'] = eat
|
||||
|
||||
@then(parse('I should have had {left} {fruits}'))
|
||||
def should_have_had_left_fruits(start, eat, left, fruits, context):
|
||||
assert isinstance(left, str)
|
||||
assert start - eat == int(left)
|
||||
assert context[fruits]['start'] == start
|
||||
assert context[fruits]['eat'] == eat
|
||||
|
||||
scenarios('outline.feature')
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.assert_outcomes(passed=4)
|
||||
|
||||
|
||||
def test_outlined_feature(testdir):
|
||||
testdir.makefile(
|
||||
".feature",
|
||||
|
|
|
@ -59,54 +59,3 @@ def test_parametrized(testdir):
|
|||
)
|
||||
result = testdir.runpytest()
|
||||
result.assert_outcomes(passed=3)
|
||||
|
||||
|
||||
def test_parametrized_with_parsers(testdir):
|
||||
"""Test parametrized scenario."""
|
||||
testdir.makefile(
|
||||
".feature",
|
||||
parametrized=textwrap.dedent(
|
||||
"""\
|
||||
Feature: Parametrized scenario
|
||||
Scenario: Parametrized given, when, thens with parsers invocation
|
||||
Given there are <start> gherkins
|
||||
When I eat <eat> gherkins
|
||||
Then I should have <left> gherkins
|
||||
"""
|
||||
),
|
||||
)
|
||||
|
||||
testdir.makepyfile(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
from pytest_bdd import given, when, then, scenario
|
||||
from pytest_bdd.parsers import re, parse
|
||||
|
||||
@pytest.mark.parametrize(["start", "eat", "left"], [(12, 5, 7)])
|
||||
@scenario("parametrized.feature", "Parametrized given, when, thens with parsers invocation")
|
||||
def test_parametrized(request, start, eat, left):
|
||||
pass
|
||||
|
||||
|
||||
@given(re("there are <start> (?P<vegetables>\\\\w+)"), target_fixture="start_vegetables")
|
||||
def start_vegetables(start, vegetables):
|
||||
return dict(start=start)
|
||||
|
||||
|
||||
@when("I eat <eat> gherkins")
|
||||
def eat_cucumbers(start_vegetables, start, eat):
|
||||
start_vegetables["eat"] = eat
|
||||
|
||||
|
||||
@then(re("I should have (?P<left>\\\\d+) (?P<vegetables>\\\\w+)"), converters=dict(left=int))
|
||||
def should_have_left_vegetables(start_vegetables, start, eat, left, vegetables):
|
||||
assert start - eat == left
|
||||
assert start_vegetables["start"] == start
|
||||
assert start_vegetables["eat"] == eat
|
||||
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.assert_outcomes(passed=1)
|
||||
|
|
Loading…
Reference in New Issue