Allow mixing feature example table with scenario example table

This commit is contained in:
Anatoly Bubenkov 2015-06-23 23:10:13 +02:00
parent a54da26578
commit 24522b8fdc
7 changed files with 83 additions and 15 deletions

View File

@ -1,6 +1,11 @@
Changelog
=========
2.13.1
------
- Allow mixing feature example table with scenario example table (bubenkoff, olegpidsadnyi)
2.13.0
------

View File

@ -575,6 +575,38 @@ among all the scenarios of that feature:
When I eat <eat> apples
Then I should have <left> apples
For some more complex case, you might want to parametrize on both levels: feature and scenario.
This is allowed as long as parameter names do not clash:
..code-block:: gherkin
Feature: Outline
Examples:
| start | eat | left |
| 12 | 5 | 7 |
| 5 | 4 | 1 |
Scenario Outline: Eat fruits
Given there are <start> <fruits>
When I eat <eat> <fruits>
Then I should have <left> <fruits>
Examples:
| fruits |
| oranges |
| apples |
Scenario Outline: Eat vegetables
Given there are <start> <vegetables>
When I eat <eat> <vegetables>
Then I should have <left> <vegetables>
Examples:
| vegetables |
| carrots |
| tomatoes |
Combine scenario outline and pytest parametrization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -3,6 +3,6 @@
from pytest_bdd.steps import given, when, then
from pytest_bdd.scenario import scenario, scenarios
__version__ = '2.13.0'
__version__ = '2.13.1'
__all__ = [given.__name__, when.__name__, then.__name__, scenario.__name__, scenarios.__name__]

View File

@ -446,13 +446,14 @@ class Scenario(object):
"""
return frozenset(sum((list(step.params) for step in self.steps), []))
def get_examples(self):
def get_example_params(self):
"""Get examples."""
return self.examples or self.feature.examples
return set(self.examples.example_params + self.feature.examples.example_params)
def get_params(self):
"""Get example params."""
return self.get_examples().get_params(self.example_converters)
for examples in [self.feature.examples, self.examples]:
yield examples.get_params(self.example_converters)
def validate(self):
"""Validate the scenario.
@ -460,11 +461,12 @@ class Scenario(object):
:raises ScenarioValidationError: when scenario is not valid
"""
params = self.params
if params and self.examples.example_params and params != set(self.examples.example_params):
example_params = self.get_example_params()
if params and example_params and params != example_params:
raise exceptions.ScenarioExamplesNotValidError(
"""Scenario "{0}" in the feature "{1}" has not valid examples. """
"""Set of step parameters {2} should match set of example values {3}.""".format(
self.name, self.feature.filename, sorted(params), sorted(self.examples.example_params),
self.name, self.feature.filename, sorted(params), sorted(example_params),
)
)

View File

@ -250,7 +250,7 @@ def _get_scenario_decorator(feature, feature_name, scenario, scenario_name, call
args = inspect.getargspec(_pytestbdd_function).args
function_args = list(args)
for arg in scenario.get_examples().example_params:
for arg in scenario.get_example_params():
if arg not in function_args:
function_args.append(arg)
if "request" not in function_args:
@ -271,9 +271,9 @@ def _get_scenario_decorator(feature, feature_name, scenario, scenario_name, call
firstlineno=caller_function.f_lineno,
)
params = scenario.get_params()
if params:
_scenario = pytest.mark.parametrize(*params)(_scenario)
for param_set in scenario.get_params():
if param_set:
_scenario = pytest.mark.parametrize(*param_set)(_scenario)
for tag in scenario.tags.union(feature.tags):
_scenario = getattr(pytest.mark, tag)(_scenario)

View File

@ -6,6 +6,11 @@ Feature: Outline
| 5 | 4 | 1 |
Scenario Outline: Outlined given, when, thens
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers
Given there are <start> <fruits>
When I eat <eat> <fruits>
Then I should have <left> <fruits>
Examples:
| fruits |
| oranges |
| apples |

View File

@ -105,11 +105,35 @@ def test_empty_example_values():
[u'start', u'eat', u'left'], [['#', '', '']])
@given('there are <start> <fruits>')
def start_fruits(start, fruits):
assert isinstance(start, int)
return {fruits: dict(start=start)}
@when('I eat <eat> <fruits>')
def eat_fruits(start_fruits, eat, fruits):
assert isinstance(eat, float)
start_fruits[fruits]['eat'] = eat
@then('I should have <left> <fruits>')
def should_have_left_fruits(start_fruits, start, eat, left, fruits):
assert isinstance(left, str)
assert start - eat == int(left)
assert start_fruits[fruits]['start'] == start
assert start_fruits[fruits]['eat'] == eat
@scenario(
'outline_feature.feature',
'Outlined given, when, thens',
example_converters=dict(start=int, eat=float, left=str)
)
def test_outlined_feature():
assert test_outlined.parametrize.args == (
[u'start', u'eat', u'left'], [[12, 5.0, '7'], [5, 4.0, '1']])
assert test_outlined_feature.parametrize.args == (
['start', 'eat', 'left'],
[[12, 5.0, '7'], [5, 4.0, '1']],
['fruits'],
[[u'oranges'], [u'apples']]
)