175 lines
5.1 KiB
Python
175 lines
5.1 KiB
Python
"""Step decorators.
|
|
|
|
Example:
|
|
|
|
@given("I have an article", target_fixture="article")
|
|
def given_article(author):
|
|
return create_test_article(author=author)
|
|
|
|
|
|
@when("I go to the article page")
|
|
def go_to_the_article_page(browser, article):
|
|
browser.visit(urljoin(browser.url, "/articles/{0}/".format(article.id)))
|
|
|
|
|
|
@then("I should not see the error message")
|
|
def no_error_message(browser):
|
|
with pytest.raises(ElementDoesNotExist):
|
|
browser.find_by_css(".message.error").first
|
|
|
|
|
|
Multiple names for the steps:
|
|
|
|
@given("I have an article")
|
|
@given("there is an article")
|
|
def article(author):
|
|
return create_test_article(author=author)
|
|
|
|
|
|
Reusing existing fixtures for a different step name:
|
|
|
|
|
|
@given("I have a beautiful article")
|
|
def given_beautiful_article(article):
|
|
pass
|
|
|
|
"""
|
|
|
|
import pytest
|
|
from _pytest.fixtures import FixtureDef
|
|
|
|
from .parsers import get_parser
|
|
from .types import GIVEN, THEN, WHEN
|
|
from .utils import get_caller_module_locals
|
|
|
|
|
|
def get_step_fixture_name(name, type_):
|
|
"""Get step fixture name.
|
|
|
|
:param name: string
|
|
:param type: step type
|
|
:return: step fixture name
|
|
:rtype: string
|
|
"""
|
|
return f"pytestbdd_{type_}_{name}"
|
|
|
|
|
|
def given(name, converters=None, target_fixture=None):
|
|
"""Given step decorator.
|
|
|
|
:param name: Step name or a parser object.
|
|
:param converters: Optional `dict` of the argument or parameter converters in form
|
|
{<param_name>: <converter function>}.
|
|
:param target_fixture: Target fixture name to replace by steps definition function.
|
|
|
|
:return: Decorator function for the step.
|
|
"""
|
|
return _step_decorator(GIVEN, name, converters=converters, target_fixture=target_fixture)
|
|
|
|
|
|
def when(name, converters=None, target_fixture=None):
|
|
"""When step decorator.
|
|
|
|
:param name: Step name or a parser object.
|
|
:param converters: Optional `dict` of the argument or parameter converters in form
|
|
{<param_name>: <converter function>}.
|
|
:param target_fixture: Target fixture name to replace by steps definition function.
|
|
|
|
:return: Decorator function for the step.
|
|
"""
|
|
return _step_decorator(WHEN, name, converters=converters, target_fixture=target_fixture)
|
|
|
|
|
|
def then(name, converters=None, target_fixture=None):
|
|
"""Then step decorator.
|
|
|
|
:param name: Step name or a parser object.
|
|
:param converters: Optional `dict` of the argument or parameter converters in form
|
|
{<param_name>: <converter function>}.
|
|
:param target_fixture: Target fixture name to replace by steps definition function.
|
|
|
|
:return: Decorator function for the step.
|
|
"""
|
|
return _step_decorator(THEN, name, converters=converters, target_fixture=target_fixture)
|
|
|
|
|
|
def _step_decorator(step_type, step_name, converters=None, target_fixture=None):
|
|
"""Step decorator for the type and the name.
|
|
|
|
:param str step_type: Step type (GIVEN, WHEN or THEN).
|
|
:param str step_name: Step name as in the feature file.
|
|
:param dict converters: Optional step arguments converters mapping
|
|
:param target_fixture: Optional fixture name to replace by step definition
|
|
|
|
:return: Decorator function for the step.
|
|
"""
|
|
|
|
def decorator(func):
|
|
step_func = func
|
|
parser_instance = get_parser(step_name)
|
|
parsed_step_name = parser_instance.name
|
|
|
|
step_func.__name__ = str(parsed_step_name)
|
|
|
|
def lazy_step_func():
|
|
return step_func
|
|
|
|
step_func.step_type = step_type
|
|
lazy_step_func.step_type = step_type
|
|
|
|
# Preserve the docstring
|
|
lazy_step_func.__doc__ = func.__doc__
|
|
|
|
step_func.parser = lazy_step_func.parser = parser_instance
|
|
if converters:
|
|
step_func.converters = lazy_step_func.converters = converters
|
|
|
|
step_func.target_fixture = lazy_step_func.target_fixture = target_fixture
|
|
|
|
lazy_step_func = pytest.fixture()(lazy_step_func)
|
|
fixture_step_name = get_step_fixture_name(parsed_step_name, step_type)
|
|
|
|
caller_locals = get_caller_module_locals()
|
|
caller_locals[fixture_step_name] = lazy_step_func
|
|
return func
|
|
|
|
return decorator
|
|
|
|
|
|
def inject_fixture(request, arg, value):
|
|
"""Inject fixture into pytest fixture request.
|
|
|
|
:param request: pytest fixture request
|
|
:param arg: argument name
|
|
:param value: argument value
|
|
"""
|
|
|
|
fd = FixtureDef(
|
|
fixturemanager=request._fixturemanager,
|
|
baseid=None,
|
|
argname=arg,
|
|
func=lambda: value,
|
|
scope="function",
|
|
params=None,
|
|
)
|
|
fd.cached_result = (value, 0, None)
|
|
|
|
old_fd = request._fixture_defs.get(arg)
|
|
add_fixturename = arg not in request.fixturenames
|
|
|
|
def fin():
|
|
request._fixturemanager._arg2fixturedefs[arg].remove(fd)
|
|
request._fixture_defs[arg] = old_fd
|
|
|
|
if add_fixturename:
|
|
request._pyfuncitem._fixtureinfo.names_closure.remove(arg)
|
|
|
|
request.addfinalizer(fin)
|
|
|
|
# inject fixture definition
|
|
request._fixturemanager._arg2fixturedefs.setdefault(arg, []).insert(0, fd)
|
|
# inject fixture value in request cache
|
|
request._fixture_defs[arg] = fd
|
|
if add_fixturename:
|
|
request._pyfuncitem._fixtureinfo.names_closure.append(arg)
|