refactor to use exec

This commit is contained in:
Anatoly Bubenkov 2014-03-13 18:32:49 +01:00
parent bca5206677
commit 05e3c0e171
5 changed files with 50 additions and 38 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -54,7 +54,6 @@ setup(
cmdclass={'test': Tox},
install_requires=[
'pytest',
'future'
],
# the following makes a plugin available to py.test
entry_points={

View File

@ -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\. """