Removing the vertical examples support

This commit is contained in:
Oleg Pidsadnyi 2022-01-15 19:18:47 +01:00
parent e0a1437908
commit 6730b428ed
6 changed files with 29 additions and 213 deletions

View File

@ -4,8 +4,11 @@ Changelog
Unreleased Unreleased
---------- ----------
This release introduces breaking changes in order to be more in line with the official gherkin specification.
- Cleanup of the documentation and tests related to parametrization (elchupanebrej) - Cleanup of the documentation and tests related to parametrization (elchupanebrej)
- Removed feature level examples for gherkin compatibility (olegpidsadnyi) - Removed feature level examples for the gherkin compatibility (olegpidsadnyi)
- Removed vertical examples for the gherkin compatibility (olegpidsadnyi)

View File

@ -522,63 +522,6 @@ Example:
| start | eat | left | | start | eat | left |
| 12 | 5 | 7 | | 12 | 5 | 7 |
pytest-bdd feature file format also supports example tables in different way:
.. code-block:: gherkin
Feature: Scenario outlines
Scenario Outline: Outlined given, when, then
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers
Examples: Vertical
| start | 12 | 2 |
| eat | 5 | 1 |
| left | 7 | 1 |
This form allows to have tables with lots of columns keeping the maximum text width predictable without significant
readability change.
The code will look like:
.. code-block:: python
from pytest_bdd import given, when, then, scenario, parsers
@scenario(
"outline.feature",
"Outlined given, when, then",
)
def test_outlined():
pass
@given(parsers.parse("there are {start:d} cucumbers", target_fixture="start_cucumbers"))
def start_cucumbers(start):
assert isinstance(start, int)
return dict(start=start)
@when(parsers.parse("I eat {eat:g} cucumbers"))
def eat_cucumbers(start_cucumbers, eat):
assert isinstance(eat, float)
start_cucumbers["eat"] = eat
@then(parsers.parse("I should have {left} cucumbers"))
def should_have_left_cucumbers(start_cucumbers, start, eat, left):
assert isinstance(left, str)
assert start - eat == int(left)
assert start_cucumbers["start"] == start
assert start_cucumbers["eat"] == eat
Example code also shows possibility to pass example converters which may be useful if you need parameter types
different than strings.
Organizing your scenarios Organizing your scenarios
------------------------- -------------------------
@ -1056,6 +999,30 @@ As as side effect, the tool will validate the files for format errors, also some
ordering of the types of the steps. ordering of the types of the steps.
.. _Migration from 5.x.x:
Migration of your tests from versions 5.x.x
-------------------------------------------
The primary focus of the pytest-bdd is the compatibility with the latest gherkin developments
e.g. multiple scenario outline example tables with tags support etc.
In order to provide the best compatibility it is best to support the features described in the official
gherkin reference. This means deprecation of some non-standard features that were implemented in pytest-bdd.
Removal of the feature examples
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The example tables on the feature level are no longer supported. The tests should be parametrized with the example tables
on the scenario level.
Removal of the vertical examples
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Vertical example tables are no longer supported since the official gherkin doesn't support them.
The example tables should have horizontal orientation.
.. _Migration from 4.x.x: .. _Migration from 4.x.x:
Migration of your tests from versions 4.x.x Migration of your tests from versions 4.x.x

View File

@ -12,7 +12,6 @@ COMMENT_RE = re.compile(r"(^|(?<=\s))#")
STEP_PREFIXES = [ STEP_PREFIXES = [
("Feature: ", types.FEATURE), ("Feature: ", types.FEATURE),
("Scenario Outline: ", types.SCENARIO_OUTLINE), ("Scenario Outline: ", types.SCENARIO_OUTLINE),
("Examples: Vertical", types.EXAMPLES_VERTICAL),
("Examples:", types.EXAMPLES), ("Examples:", types.EXAMPLES),
("Scenario: ", types.SCENARIO), ("Scenario: ", types.SCENARIO),
("Background:", types.BACKGROUND), ("Background:", types.BACKGROUND),
@ -157,26 +156,11 @@ def parse_feature(basedir: str, filename: str, encoding: str = "utf-8") -> "Feat
elif mode == types.EXAMPLES: elif mode == types.EXAMPLES:
mode = types.EXAMPLES_HEADERS mode = types.EXAMPLES_HEADERS
scenario.examples.line_number = line_number scenario.examples.line_number = line_number
elif mode == types.EXAMPLES_VERTICAL:
mode = types.EXAMPLE_LINE_VERTICAL
scenario.examples.line_number = line_number
elif mode == types.EXAMPLES_HEADERS: elif mode == types.EXAMPLES_HEADERS:
scenario.examples.set_param_names([l for l in split_line(parsed_line) if l]) scenario.examples.set_param_names([l for l in split_line(parsed_line) if l])
mode = types.EXAMPLE_LINE mode = types.EXAMPLE_LINE
elif mode == types.EXAMPLE_LINE: elif mode == types.EXAMPLE_LINE:
scenario.examples.add_example([l for l in split_line(stripped_line)]) scenario.examples.add_example([l for l in split_line(stripped_line)])
elif mode == types.EXAMPLE_LINE_VERTICAL:
param_line_parts = [l for l in split_line(stripped_line)]
try:
scenario.examples.add_example_row(param_line_parts[0], param_line_parts[1:])
except exceptions.ExamplesNotValidError as exc:
raise exceptions.FeatureError(
f"Scenario has not valid examples. {exc.args[0]}",
line_number,
clean_line,
filename,
)
elif mode and mode not in (types.FEATURE, types.TAG): elif mode and mode not in (types.FEATURE, types.TAG):
step = Step(name=parsed_line, type=mode, indent=line_indent, line_number=line_number, keyword=keyword) step = Step(name=parsed_line, type=mode, indent=line_indent, line_number=line_number, keyword=keyword)
if feature.background and not scenario: if feature.background and not scenario:
@ -383,7 +367,6 @@ class Examples:
"""Initialize examples instance.""" """Initialize examples instance."""
self.example_params = [] self.example_params = []
self.examples = [] self.examples = []
self.vertical_examples = []
self.line_number = None self.line_number = None
self.name = None self.name = None
@ -412,17 +395,8 @@ class Examples:
f"""Example rows should contain unique parameters. "{param}" appeared more than once""" f"""Example rows should contain unique parameters. "{param}" appeared more than once"""
) )
self.example_params.append(param) self.example_params.append(param)
self.vertical_examples.append(values)
def as_contexts(self) -> typing.Iterable[typing.Dict[str, typing.Any]]: def as_contexts(self) -> typing.Iterable[typing.Dict[str, typing.Any]]:
param_count = len(self.example_params)
if self.vertical_examples and not self.examples:
for value_index in range(len(self.vertical_examples[0])):
example = []
for param_index in range(param_count):
example.append(self.vertical_examples[param_index][value_index])
self.examples.append(example)
if not self.examples: if not self.examples:
return return
@ -430,12 +404,11 @@ class Examples:
for row in rows: for row in rows:
assert len(header) == len(row) assert len(header) == len(row)
yield dict(zip(header, row)) yield dict(zip(header, row))
def __bool__(self): def __bool__(self):
"""Bool comparison.""" """Bool comparison."""
return bool(self.vertical_examples or self.examples) return bool(self.examples)
def get_tags(line): def get_tags(line):

View File

@ -3,10 +3,8 @@
FEATURE = "feature" FEATURE = "feature"
SCENARIO_OUTLINE = "scenario outline" SCENARIO_OUTLINE = "scenario outline"
EXAMPLES = "examples" EXAMPLES = "examples"
EXAMPLES_VERTICAL = "examples vertical"
EXAMPLES_HEADERS = "example headers" EXAMPLES_HEADERS = "example headers"
EXAMPLE_LINE = "example line" EXAMPLE_LINE = "example line"
EXAMPLE_LINE_VERTICAL = "example line vertical"
SCENARIO = "scenario" SCENARIO = "scenario"
BACKGROUND = "background" BACKGROUND = "background"
GIVEN = "given" GIVEN = "given"

View File

@ -122,46 +122,6 @@ def test_wrongly_outlined(testdir):
result.stdout.fnmatch_lines("*should match set of example values [[]'eat', 'left', 'start', 'unknown_param'[]].*") result.stdout.fnmatch_lines("*should match set of example values [[]'eat', 'left', 'start', 'unknown_param'[]].*")
def test_wrong_vertical_examples_scenario(testdir):
"""Test parametrized scenario vertical example table has wrong format."""
testdir.makefile(
".feature",
outline=textwrap.dedent(
"""\
Feature: Outline
Scenario Outline: Outlined with wrong vertical example table
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers
Examples: Vertical
| start | 12 | 2 |
| start | 10 | 1 |
| left | 7 | 1 |
"""
),
)
testdir.makeconftest(textwrap.dedent(STEPS))
testdir.makepyfile(
textwrap.dedent(
"""\
from pytest_bdd import scenario
@scenario("outline.feature", "Outlined with wrong vertical example table")
def test_outline(request):
pass
"""
)
)
result = testdir.runpytest()
assert_outcomes(result, errors=1)
result.stdout.fnmatch_lines(
"*Scenario has not valid examples. Example rows should contain unique parameters. "
'"start" appeared more than once.*'
)
def test_outlined_with_other_fixtures(testdir): def test_outlined_with_other_fixtures(testdir):
"""Test outlined scenario also using other parametrized fixture.""" """Test outlined scenario also using other parametrized fixture."""
testdir.makefile( testdir.makefile(
@ -211,54 +171,6 @@ def test_outlined_with_other_fixtures(testdir):
result.assert_outcomes(passed=6) result.assert_outcomes(passed=6)
def test_vertical_example(testdir):
"""Test outlined scenario with vertical examples table."""
testdir.makefile(
".feature",
outline=textwrap.dedent(
"""\
Feature: Outline
Scenario Outline: Outlined with vertical example table
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers
Examples: Vertical
| start | 12 | 2 |
| eat | 5 | 1 |
| left | 7 | 1 |
"""
),
)
testdir.makeconftest(textwrap.dedent(STEPS))
testdir.makepyfile(
textwrap.dedent(
"""\
from pytest_bdd import scenario
@scenario(
"outline.feature",
"Outlined with vertical example table",
)
def test_outline():
pass
"""
)
)
result = testdir.runpytest("-s")
result.assert_outcomes(passed=2)
parametrizations = collect_dumped_objects(result)
# fmt: off
assert parametrizations == [
12, 5.0, "7",
2, 1.0, "1",
]
# fmt: on
def test_outline_with_escaped_pipes(testdir): def test_outline_with_escaped_pipes(testdir):
"""Test parametrized feature example table with escaped pipe characters in input.""" """Test parametrized feature example table with escaped pipe characters in input."""
testdir.makefile( testdir.makefile(

View File

@ -61,40 +61,3 @@ def test_scenario_with_empty_example_values(testdir):
result = testdir.runpytest("-s") result = testdir.runpytest("-s")
result.assert_outcomes(passed=1) result.assert_outcomes(passed=1)
assert collect_dumped_objects(result) == ["#", "", ""] assert collect_dumped_objects(result) == ["#", "", ""]
def test_scenario_with_empty_example_values_vertical(testdir):
testdir.makefile(
".feature",
outline=textwrap.dedent(
"""\
Feature: Outline
Scenario Outline: Outlined with empty example values vertical
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers
Examples: Vertical
| start | # |
| eat | |
| left | |
"""
),
)
testdir.makeconftest(textwrap.dedent(STEPS))
testdir.makepyfile(
textwrap.dedent(
"""\
from pytest_bdd.utils import dump_obj
from pytest_bdd import scenario
@scenario("outline.feature", "Outlined with empty example values vertical")
def test_outline():
pass
"""
)
)
result = testdir.runpytest("-s")
result.assert_outcomes(passed=1)
assert collect_dumped_objects(result) == ["#", "", ""]