cleanup unnecessary code, move to more clean implementation

This commit is contained in:
Anatoly Bubenkov 2013-08-11 23:34:06 +02:00
parent 3011f19ab3
commit 1f9bbc8cde
7 changed files with 154 additions and 83 deletions

View File

@ -108,16 +108,17 @@ As you can see we don't use Scenario Outline, but use just Scenario, just becaus
The code will look like:
test_reuse = scenario(
# here we use pytest power to parametrize test
@pytest.mark.parametrize(
['start', 'eat', 'left'],
[(12, 5, 7)])
@scenario(
'parametrized.feature',
'Parametrized given, when, thens',
# here we tell scenario about the parameters, it's not possible to get them automatically, as
# feature files are parsed on test runtime, not import time
params=['start', 'eat', 'left']
)
# here we use pytest power to parametrize test
test_reuse = pytest.mark.parametrize(['start', 'eat', 'left'], [(12, 5, 7)])(test_reuse)
# note that we should receive same arguments in function that we use for test parametrization
def test_parametrized(start, eat, left):
"""We don't need to do anything here, everything will be managed by scenario decorator."""
@given('there are <start> cucumbers')
@ -135,6 +136,7 @@ The code will look like:
assert start - eat == left
Reuse fixtures
================

View File

@ -43,7 +43,7 @@ STEP_PREFIXES = {
COMMENT_SYMBOLS = '#'
STEP_PARAM_RE = re.compile('\<(.+)\>')
STEP_PARAM_RE = re.compile('\<(.+?)\>')
def get_step_type(line):

View File

@ -13,16 +13,20 @@ test_publish_article = scenario(
import inspect # pragma: no cover
from os import path as op # pragma: no cover
from _pytest import python
from pytest_bdd.feature import Feature # pragma: no cover
from pytest_bdd.steps import recreate_function # pragma: no cover
from pytest_bdd.steps import recreate_function
class ScenarioNotFound(Exception): # pragma: no cover
"""Scenario Not Found"""
def scenario(feature_name, scenario_name, params=()):
"""Scenario."""
def scenario(feature_name, scenario_name):
"""Scenario. May be called both as decorator and as just normal function"""
def decorator(request):
def _scenario(request):
# Get the feature
@ -34,16 +38,31 @@ def scenario(feature_name, scenario_name, params=()):
try:
scenario = feature.scenarios[scenario_name]
except KeyError:
raise ScenarioNotFound('Scenario "{0}" in feature "{1}" is not found'.format(scenario_name, feature_name))
raise ScenarioNotFound(
'Scenario "{0}" in feature "{1}" is not found'.format(scenario_name, feature_name))
if scenario.params != _scenario.pytestbdd_params:
raise Exception(scenario.params, _scenario.pytestbdd_params)
# Execute scenario's steps
for step in scenario.steps:
func = request.getfuncargvalue(step)
kwargs = dict((arg, request.getfuncargvalue(arg)) for arg in inspect.getargspec(func).args)
func(**kwargs)
step_func = request.getfuncargvalue(step)
kwargs = dict((arg, request.getfuncargvalue(arg)) for arg in inspect.getargspec(step_func).args)
step_func(**kwargs)
if params:
# add test parameters to function
_scenario = recreate_function(_scenario, add_args=params)
_scenario.pytestbdd_params = set()
if isinstance(request, python.FixtureRequest):
# we called as a normal function
return _scenario(request)
# we called as a decorator, so modify the returned function to add parameters from a decorated function
func_args = inspect.getargspec(request).args
if 'request' in func_args:
func_args.remove('request')
_scenario = recreate_function(_scenario, add_args=func_args)
_scenario.pytestbdd_params = set(func_args)
return _scenario
return decorator

View File

@ -38,7 +38,7 @@ import sys
import pytest
from pytest_bdd.feature import remove_prefix
from pytest_bdd.feature import remove_prefix, get_step_params
from pytest_bdd.types import GIVEN, WHEN, THEN
PY3 = sys.version_info[0] >= 3
@ -48,6 +48,10 @@ class StepError(Exception):
pass
class NotEnoughStepParams(Exception):
pass
def given(name, fixture=None):
"""Given step decorator.
@ -95,11 +99,10 @@ def _not_a_fixture_decorator(func):
raise StepError('Cannot be used as a decorator when the fixture is specified')
def _step_decorator(step_type, step_name, params=None):
def _step_decorator(step_type, step_name):
"""Step decorator for the type and the name.
:param step_type: Step type (GIVEN, WHEN or THEN).
:param step_name: Step name as in the feature file.
:param params: Step params.
:return: Decorator function for the step.
@ -108,8 +111,17 @@ def _step_decorator(step_type, step_name, params=None):
"""
step_name = remove_prefix(step_name)
step_params = set(get_step_params(step_name))
def decorator(func):
step_func = func
if step_params:
step_func_args = inspect.getargspec(step_func).args
if step_params.intersection(step_func_args) != step_params:
raise NotEnoughStepParams(
"""Step "{0}" doesn't have enough parameters declared.
Should declare params: {1}, but declared only: {2}""".format(step_name, step_params, step_func_args))
if step_type == GIVEN:
if not hasattr(func, '_pytestfixturefunction'):
# avoid overfixturing of a fixture
@ -158,7 +170,8 @@ def recreate_function(func, module=None, add_args=()):
elif arg == 'co_argcount':
args.append(getattr(code, arg) + len(add_args))
elif arg == 'co_varnames':
args.append(tuple(add_args) + getattr(code, arg))
co_varnames = getattr(code, arg)
args.append(co_varnames[:code.co_argcount] + tuple(add_args) + co_varnames[code.co_argcount:])
else:
args.append(getattr(code, arg))

View File

@ -1,3 +1,4 @@
mock
pytest-pep8
pytest-cov
pytest-cache

View File

@ -1,16 +1,52 @@
import pytest
from pytest_bdd.steps import when
from pytest_bdd.steps import NotEnoughStepParams
from pytest_bdd import given, then, scenario
from pytest_bdd import given, when, then, scenario
test_reuse = scenario(
@pytest.mark.parametrize(
['start', 'eat', 'left'],
[(12, 5, 7)])
@scenario(
'parametrized.feature',
'Parametrized given, when, thens',
params=['start', 'eat', 'left']
)
def test_parametrized(request, start, eat, left):
"""Test parametrized scenario."""
test_reuse = pytest.mark.parametrize(['start', 'eat', 'left'], [(12, 5, 7)])(test_reuse)
def test_parametrized_given():
"""Test parametrized given."""
with pytest.raises(NotEnoughStepParams) as exc:
@given('there are <some> cucumbers')
def some_cucumbers():
return {}
assert exc.value.args == (
'Step "there are <some> cucumbers" doesn\'t have enough parameters declared.\n'
'Should declare params: set([\'some\']), but declared only: []',)
def test_parametrized_when():
"""Test parametrized when."""
with pytest.raises(NotEnoughStepParams) as exc:
@when('I eat <some> cucumbers')
def some_cucumbers():
return {}
assert exc.value.args == (
'Step "I eat <some> cucumbers" doesn\'t have enough parameters declared.\n'
'Should declare params: set([\'some\']), but declared only: []',)
def test_parametrized_then():
"""Test parametrized then."""
with pytest.raises(NotEnoughStepParams) as exc:
@when('I should have <some> cucumbers')
def some_cucumbers():
return {}
assert exc.value.args == (
'Step "I should have <some> cucumbers" doesn\'t have enough parameters declared.\n'
'Should declare params: set([\'some\']), but declared only: []',)
@given('there are <start> cucumbers')