diff --git a/pytest_bdd/mark.py b/pytest_bdd/mark.py index 5fb7449..99b8d84 100644 --- a/pytest_bdd/mark.py +++ b/pytest_bdd/mark.py @@ -1,7 +1,9 @@ """Pytest-bdd markers.""" import inspect -from pytest_bdd.steps import recreate_function, get_caller_module, get_caller_function +import pytest + +from pytest_bdd.steps import execute, recreate_function, get_caller_module, get_caller_function from pytest_bdd import scenario as bdd_scenario @@ -19,7 +21,28 @@ def scenario(feature_name, scenario_name, encoding='utf-8', example_converters=N args = inspect.getargspec(request).args - _scenario = recreate_function(_scenario, name=request.__name__, module=caller_module, add_args=args) + if 'request' not in args: + args.insert(0, 'request') + + g = globals().copy() + g.update(locals()) + + pytestbdd_params = _scenario.pytestbdd_params + scenario = _scenario.scenario + + sc_args = list(scenario.example_params) + if 'request' not in sc_args: + sc_args.insert(0, 'request') + + code = """def _decorated_scenario({0}): + _scenario({1})""".format(', '.join(args), ', '.join(sc_args)) + + execute(code, g) + + _scenario = recreate_function(g['_decorated_scenario'], module=caller_module, add_args=args) + + if pytestbdd_params: + _scenario = pytest.mark.parametrize(*pytestbdd_params)(_scenario) return _scenario diff --git a/pytest_bdd/scenario.py b/pytest_bdd/scenario.py index eaeea31..3279d2f 100644 --- a/pytest_bdd/scenario.py +++ b/pytest_bdd/scenario.py @@ -11,22 +11,20 @@ test_publish_article = scenario( ) """ + import collections import os import imp -import sys import inspect # pragma: no cover from os import path as op # pragma: no cover import pytest -from future import utils as future_utils - from _pytest import python from pytest_bdd.feature import Feature, force_encode # pragma: no cover -from pytest_bdd.steps import recreate_function, get_caller_module, get_caller_function +from pytest_bdd.steps import execute, recreate_function, get_caller_module, get_caller_function from pytest_bdd.types import GIVEN from pytest_bdd import plugin @@ -110,7 +108,6 @@ def _find_step_function(request, name, encoding): pattern = getattr(fixturedef.func, 'pattern', None) match = pattern.match(name) if pattern else None - if match: converters = getattr(fixturedef.func, 'converters', {}) for arg, value in match.groupdict().items(): @@ -121,7 +118,7 @@ def _find_step_function(request, name, encoding): raise -def _validate_scenario(feature, scenario, request): +def _validate_scenario(feature, scenario): """Validate the scenario.""" if scenario.params and scenario.example_params and scenario.params != set(scenario.example_params): raise ScenarioExamplesNotValidError( @@ -132,21 +129,6 @@ def _validate_scenario(feature, scenario, request): ) -def _execute_scenario_outline(feature, scenario, request, encoding, example_converters=None): - """Execute the scenario outline.""" - errors = [] - # tricky part, basically here we clear pytest request cache - for example in scenario.examples: - request._funcargs = {} - request._arg2index = {} - try: - _execute_scenario(feature, scenario, request, encoding, example=dict(zip(scenario.example_params, example))) - except Exception as e: - errors.append([e, sys.exc_info()[2]]) - for error in errors: - raise future_utils.raise_with_traceback(error[0], error[1]) - - def _execute_step_function(request, feature, step, step_func, example=None): """Execute step function.""" kwargs = {} @@ -177,8 +159,6 @@ def _execute_step_function(request, feature, step, step_func, example=None): def _execute_scenario(feature, scenario, request, encoding, example=None): """Execute the scenario.""" - _validate_scenario(feature, scenario, request) - givens = set() # Execute scenario steps for step in scenario.steps: @@ -265,6 +245,8 @@ def scenario( 'Scenario "{0}" in feature "{1}" is not found.'.format(scenario_name, feature_name) ) + _validate_scenario(feature, scenario) + if scenario.examples: params = [] for example in scenario.examples: @@ -276,12 +258,18 @@ def scenario( else: params = [] - def _scenario(request, *args, **kwargs): - _execute_scenario(feature, scenario, request, encoding) + g = globals().copy() + g.update(locals()) + + code = """def _scenario(request, {0}): + _execute_scenario(feature, scenario, request, encoding)""".format(','.join(scenario.example_params)) + + execute(code, g) _scenario = recreate_function( - _scenario, module=caller_module, firstlineno=caller_function.f_lineno, - add_args=scenario.example_params) + g['_scenario'], module=caller_module, firstlineno=caller_function.f_lineno) if params: _scenario = pytest.mark.parametrize(*params)(_scenario) + _scenario.pytestbdd_params = params + _scenario.scenario = scenario return _scenario diff --git a/pytest_bdd/steps.py b/pytest_bdd/steps.py index f5b77e4..a31c7c8 100644 --- a/pytest_bdd/steps.py +++ b/pytest_bdd/steps.py @@ -234,7 +234,6 @@ def contribute_to_module(module, name, func): """ func = recreate_function(func, module=module) - setattr(module, name, func) @@ -247,3 +246,7 @@ def get_caller_module(depth=2): def get_caller_function(depth=2): """Return caller function.""" return sys._getframe(depth) + + +def execute(code, g): + exec(code, g) diff --git a/setup.py b/setup.py index 5c87d29..5e659fc 100755 --- a/setup.py +++ b/setup.py @@ -54,7 +54,6 @@ setup( cmdclass={'test': Tox}, install_requires=[ 'pytest', - 'future' ], # the following makes a plugin available to py.test entry_points={ diff --git a/tests/feature/test_outline.py b/tests/feature/test_outline.py index edb8fa2..570e9f1 100644 --- a/tests/feature/test_outline.py +++ b/tests/feature/test_outline.py @@ -37,15 +37,14 @@ def should_have_left_cucumbers(start_cucumbers, start, eat, left): def test_wrongly_outlined(request): """Test parametrized scenario when the test function lacks parameters.""" - @mark.scenario( - 'outline.feature', - 'Outlined with wrong examples', - ) - def wrongly_outlined(request): - pass with pytest.raises(ScenarioExamplesNotValidError) as exc: - wrongly_outlined(request, 1, 2, 3, 4) + @mark.scenario( + 'outline.feature', + 'Outlined with wrong examples', + ) + def wrongly_outlined(request): + pass assert re.match( """Scenario \"Outlined with wrong examples\" in the feature \"(.+)\" has not valid examples\. """