Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #271: Use parametrize markers params for getting example_kwargs #272

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions pytest_bdd/reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import time

from .feature import force_unicode
from .utils import get_parametrize_markers_args
from .utils import get_parametrize_markers_args, get_parametrize_params


class StepReport(object):
Expand Down Expand Up @@ -73,20 +73,30 @@ def __init__(self, scenario, node):
"""
self.scenario = scenario
self.step_reports = []
self.param_index = None

parametrize_args = get_parametrize_markers_args(node)
if parametrize_args and scenario.examples:
param_names = parametrize_args[0] if isinstance(parametrize_args[0], (tuple, list)) else [
parametrize_args[0]]
param_values = parametrize_args[1]
params = get_parametrize_params(parametrize_args)

self.param_index = self.get_param_index(node, params)
self.example_kwargs = self.get_example_kwargs(node, params)

def get_param_index(self, node, params):
if params:
param_names = params[0]['names']
param_values = params[0]['values']
node_param_values = [node.funcargs[param_name] for param_name in param_names]
if node_param_values in param_values:
self.param_index = param_values.index(node_param_values)
return param_values.index(node_param_values)
elif tuple(node_param_values) in param_values:
self.param_index = param_values.index(tuple(node_param_values))
self.example_kwargs = {
example_param: force_unicode(node.funcargs[example_param])
for example_param in scenario.get_example_params()
return param_values.index(tuple(node_param_values))
return None

def get_example_kwargs(self, node, params):
params_names = (param['names'] for param in params)
all_names = sum(params_names, [])
return {
example_param_name: force_unicode(node.funcargs[example_param_name])
for example_param_name in all_names
}

@property
Expand Down
30 changes: 30 additions & 0 deletions pytest_bdd/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,33 @@ def get_markers_args_using_iter_markers(node, mark_name):
def get_markers_args_using_get_marker(node, mark_name):
"""Deprecated on pytest>=3.6"""
return getattr(node.get_marker(mark_name), 'args', ())


def get_parametrize_params(parametrize_args):
"""Group parametrize markers arguments names and values.

:param parametrize_args: parametrize markers arguments.
:return: `list` of `dict` in the form of:
[
{
"names": ["name1", "name2", ...],
"values": [value1, value2, ...],
},
...
]
"""
params = []
for i in range(0, len(parametrize_args), 2):
params.append({
'names': _coerce_list(parametrize_args[i]),
'values': parametrize_args[i+1]
})
return params


def _coerce_list(names):
if not isinstance(names, (tuple, list)):
# As pytest.mark.parametrize has only one param name,
# it is not returned as a list. Convert it to list:
names = [names]
return list(names)
12 changes: 12 additions & 0 deletions tests/feature/gherkin_terminal_reporter.feature
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,15 @@ Feature: Gherkin terminal reporter
Given there is gherkin scenario outline implemented
When I run tests with step expanded mode
Then output must contain parameters values

Scenario: Should handle unicode output in expanded mode
Given there is gherkin scenario that has unicode characters
When I run tests with step expanded mode
Then UnicodeEncodeError is not raised during test execution
And output should contain single passing test case

Scenario: Should handle test parametrized using @pytest.mark.parametrize decorator in expanded mode
Given there is gherkin scenario that has parametrized scenario
When I run tests with step expanded mode
Then KeyError is not raised during test execution
And output should contain single passing test case
1 change: 1 addition & 0 deletions tests/feature/outline_feature.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Feature: Outline
| start | eat | left |
| 12 | 5 | 7 |
| 5 | 4 | 1 |
| 4 | 2 | 2 |

Scenario Outline: Outlined given, when, thens
Given there are <start> <fruits>
Expand Down
6 changes: 6 additions & 0 deletions tests/feature/parametrized.feature
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ Scenario: Parametrized given, when, thens
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers


Scenario: Parametrized given, then - single parameter name
Given there are <start> cucumbers
When I do not eat any cucumber
Then I still should have <start> cucumbers
116 changes: 99 additions & 17 deletions tests/feature/test_gherkin_terminal_reporter.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# coding: utf-8
import re


import pytest

from pytest_bdd import scenario, given, when, then
from tests.utils import get_test_filepath, prepare_feature_and_py_files
from pytest_bdd import given, parsers, scenario, then, when


@scenario('gherkin_terminal_reporter.feature',
Expand Down Expand Up @@ -61,6 +59,18 @@ def test_Should_step_parameters_be_replaced_by_their_values():
pass


@scenario('gherkin_terminal_reporter.feature',
'Should handle unicode output in expanded mode')
def test_Should_handle_unicode_output_in_expanded_mode():
pass


@scenario('gherkin_terminal_reporter.feature',
'Should handle test parametrized using @pytest.mark.parametrize decorator in expanded mode')
def test_Should_handle_test_parametrized_using_pytest_mark_parametrize_decorator_in_expanded_mode():
pass


@pytest.fixture(params=[0, 1, 2],
ids=['compact mode', 'line per test', 'verbose'])
def verbosity_mode(request):
Expand Down Expand Up @@ -152,6 +162,85 @@ def test_scenario_2():
return example


@given("there is gherkin scenario that has unicode characters")
def gherkin_scenario_that_has_unicode_characters(testdir):
testdir.makefile('.feature', test="""
Feature: Юнікодні символи

Scenario: Кроки в .feature файлі містять юнікод
Given у мене є рядок який містить 'якийсь контент'
Then I should see that the string equals to content 'якийсь контент'
""")
testdir.makepyfile(test_gherkin="""
# coding: utf-8
import functools
import sys

import pytest

from pytest_bdd import given, parsers, scenario, then

scenario = functools.partial(scenario, 'test.feature')

@scenario('Кроки в .feature файлі містять юнікод')
def test_steps_in_feature_file_have_unicode():
pass

@pytest.fixture
def string():
return {'content': ''}

@given(parsers.parse(u"у мене є рядок який містить '{content}'"))
def there_is_a_string_with_content(content, string):
string['content'] = content

@then(parsers.parse("I should see that the string equals to content '{content}'"))
def assert_that_the_string_equals_to_content(content, string):
assert string['content'] == content
if sys.version_info < (3, 0):
assert isinstance(content, unicode)
""")


@given("there is gherkin scenario that has parametrized scenario")
def gherkin_scenario_that_has_parametrized_scenario(testdir):
testdir.makefile('.feature', test="""
Scenario: Parametrized given, when, thens
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers
""")
testdir.makepyfile(test_gherkin="""
import pytest

from pytest_bdd import given, when, then, scenario

@pytest.mark.parametrize(
['start', 'eat', 'left'],
[(12, 5, 7)])
@scenario(
'test.feature',
'Parametrized given, when, thens',
)
def test_parametrized(request, start, eat, left):
pass

@given('there are <start> cucumbers')
def start_cucumbers(start):
return dict(start=start)

@when('I eat <eat> cucumbers')
def eat_cucumbers(start_cucumbers, start, eat):
start_cucumbers['eat'] = eat

@then('I should have <left> cucumbers')
def should_have_left_cucumbers(start_cucumbers, start, eat, left):
assert start - eat == left
assert start_cucumbers['start'] == start
assert start_cucumbers['eat'] == eat
""")


@when("I run tests")
def run_tests(testdir, test_execution):
test_execution['regular'] = testdir.runpytest()
Expand Down Expand Up @@ -347,20 +436,13 @@ def output_output_must_contain_parameters_values(test_execution, gherkin_scenari
ghe.stdout.fnmatch_lines('*PASSED')


@pytest.mark.parametrize(
'feature_file, py_file, name', [
('./steps/unicode.feature', './steps/test_unicode.py', 'test_steps_in_feature_file_have_unicode')
]
)
def test_scenario_in_expanded_mode(testdir, test_execution, feature_file, py_file, name):
prepare_feature_and_py_files(testdir, feature_file, py_file)
@then(parsers.parse('{error} is not raised during test execution'))
def error_is_not_raised_during_test_execution(test_execution, error):
ghe = test_execution['gherkin']
assert error not in ghe.stdout.str()

test_execution['gherkin'] = testdir.runpytest(
'-k %s' % name,
'--gherkin-terminal-reporter',
'--gherkin-terminal-reporter-expanded',
'-vv',
)

@then("output should contain single passing test case")
def output_must_contain_single_passing_test_case(test_execution):
ghe = test_execution['gherkin']
ghe.assert_outcomes(passed=1)
2 changes: 1 addition & 1 deletion tests/feature/test_outline.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def should_have_left_fruits(start_fruits, start, eat, left, fruits):
def test_outlined_feature(request):
assert get_parametrize_markers_args(request.node) == (
['start', 'eat', 'left'],
[[12, 5.0, '7'], [5, 4.0, '1']],
[[12, 5.0, '7'], [5, 4.0, '1'], [4, 2.0, '2']],
youtux marked this conversation as resolved.
Show resolved Hide resolved
['fruits'],
[[u'oranges'], [u'apples']]
)
21 changes: 21 additions & 0 deletions tests/feature/test_parametrized.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ def test_parametrized(request, start, eat, left):
"""Test parametrized scenario."""


@pytest.mark.parametrize(
'start', [12, 5]
)
@scenario(
'parametrized.feature',
'Parametrized given, then - single parameter name',
)
def test_parametrized_single_parameter_name(request, start):
"""Test parametrized scenario."""


@pytest.fixture(params=[1, 2])
def foo_bar(request):
return 'bar' * request.param
Expand All @@ -40,8 +51,18 @@ def eat_cucumbers(start_cucumbers, start, eat):
start_cucumbers['eat'] = eat


@when('I do not eat any cucumber')
def do_not_eat_any_cucumber():
pass


@then('I should have <left> cucumbers')
def should_have_left_cucumbers(start_cucumbers, start, eat, left):
assert start - eat == left
assert start_cucumbers['start'] == start
assert start_cucumbers['eat'] == eat


@then('I still should have <start> cucumbers')
def still_should_have_start_cucumbers(start_cucumbers, start):
assert start_cucumbers['start'] == start
18 changes: 0 additions & 18 deletions tests/utils.py

This file was deleted.