Skip to content

Commit

Permalink
[pre-commit.ci] auto fixes from pre-commit.com hooks
Browse files Browse the repository at this point in the history
for more information, see https://pre-commit.ci
  • Loading branch information
pre-commit-ci[bot] authored and arunpkm committed Apr 24, 2023
1 parent 0959385 commit db90668
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 18 deletions.
21 changes: 16 additions & 5 deletions src/pytest_bdd/scenario.py
Expand Up @@ -162,11 +162,22 @@ def _execute_step_function(
raise

if context.target_fixture is not None:
target_fixture_tokens = [token for token in context.target_fixture.split(',') if token]
return_values = (return_value,) if not isinstance(return_value, tuple) else return_value
assert len(target_fixture_tokens) == len(return_values), f"Return value count: {len(return_values)} are not matching target_fixture count: {len(target_fixture_tokens)}"
for token, value in zip(target_fixture_tokens, return_values):
inject_fixture(request, token, value)
if isinstance(return_value, Exception):
arg = context.target_exception if context.target_exception else "response"
inject_fixture(request=request, arg=arg, value=return_value)
else:
target_fixture_tokens = [token for token in context.target_fixture.split(",") if token]
# Single return value in target_fixture
if len(target_fixture_tokens) == 1:
inject_fixture(request=request, arg=target_fixture_tokens[0], value=return_value)
# Multiple comma separated return values in target_fixture
else:
return_values = (return_value,) if not isinstance(return_value, tuple) else return_value
assert len(target_fixture_tokens) == len(
return_values
), f"Return value count: {len(return_values)} are not matching target_fixture count: {len(target_fixture_tokens)}"
for token, value in zip(target_fixture_tokens, return_values):
inject_fixture(request=request, arg=token, value=value)

request.config.hook.pytest_bdd_after_step(**kw)

Expand Down
8 changes: 7 additions & 1 deletion src/pytest_bdd/steps.py
Expand Up @@ -66,6 +66,7 @@ class StepFunctionContext:
parser: StepParser
converters: dict[str, Callable[..., Any]] = field(default_factory=dict)
target_fixture: str | None = None
target_exception: str | None = None


def get_step_fixture_name(step: Step) -> str:
Expand Down Expand Up @@ -96,6 +97,7 @@ def when(
name: str | StepParser,
converters: dict[str, Callable] | None = None,
target_fixture: str | None = None,
target_exception: str | None = None,
stacklevel: int = 1,
) -> Callable:
"""When step decorator.
Expand All @@ -104,11 +106,12 @@ def when(
: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.
:param target_exception: Target exception name to receive Exception object
:param stacklevel: Stack level to find the caller frame. This is used when injecting the step definition fixture.
:return: Decorator function for the step.
"""
return step(name, WHEN, converters=converters, target_fixture=target_fixture, stacklevel=stacklevel)
return step(name, WHEN, converters=converters, target_fixture=target_fixture, target_exception=target_exception, stacklevel=stacklevel)


def then(
Expand All @@ -135,6 +138,7 @@ def step(
type_: Literal["given", "when", "then"] | None = None,
converters: dict[str, Callable] | None = None,
target_fixture: str | None = None,
target_exception: str | None = None,
stacklevel: int = 1,
) -> Callable[[TCallable], TCallable]:
"""Generic step decorator.
Expand All @@ -143,6 +147,7 @@ def step(
:param type_: Step type ("given", "when" or "then"). If None, this step will work for all the types.
:param converters: Optional step arguments converters mapping.
:param target_fixture: Optional fixture name to replace by step definition.
:param target_exception: Optional target exception name
:param stacklevel: Stack level to find the caller frame. This is used when injecting the step definition fixture.
:return: Decorator function for the step.
Expand All @@ -165,6 +170,7 @@ def decorator(func: TCallable) -> TCallable:
parser=parser,
converters=converters,
target_fixture=target_fixture,
target_exception=target_exception,
)

def step_function_marker() -> StepFunctionContext:
Expand Down
46 changes: 46 additions & 0 deletions tests/feature/test_steps.py
Expand Up @@ -584,3 +584,49 @@ def _(stuff):
"*Tearing down...*",
]
)


def test_when_exception(pytester):
pytester.makefile(
".feature",
when_exception=textwrap.dedent(
"""\
Feature: When exception
Scenario: Test when exception is generated
When I have injected exception
Then Exception object should be received as default 'response' param
When I have injected exception with target_exception='exception_ret'
Then Exception object should be received as target_exception='exception_ret'
"""
),
)
pytester.makepyfile(
textwrap.dedent(
"""\
import pytest
from pytest_bdd import when, then, scenario
@scenario("when_exception.feature", "Test when exception is generated")
def test_when_exception():
pass
@when("I have injected exception", target_fixture="foo")
def _():
return Exception("Dummy Exception obj")
@when("I have injected exception with target_exception='exception_ret'", target_fixture="foo", target_exception="exception_ret")
def _():
return Exception("Dummy Exception obj")
@then("Exception object should be received as default 'response' param")
def _(response):
assert isinstance(response, Exception), "response is not Exception object"
@then("Exception object should be received as target_exception='exception_ret'")
def _(exception_ret):
assert isinstance(exception_ret, Exception), "response is not Exception object"
"""
)
)
result = pytester.runpytest()
result.assert_outcomes(passed=1)
33 changes: 21 additions & 12 deletions tests/steps/test_given.py
Expand Up @@ -2,15 +2,17 @@
import textwrap


def test_given_injection(pytester):
def test_given_injection_single_value(pytester):
pytester.makefile(
".feature",
given=textwrap.dedent(
"""\
Feature: Given
Scenario: Test given fixture injection
Given I have injecting given
Given I have injected single value
Then foo should be "injected foo"
Given I have injected tuple value
Then foo should be tuple value
"""
),
)
Expand All @@ -24,31 +26,37 @@ def test_given_injection(pytester):
def test_given():
pass
@given("I have injecting given", target_fixture="foo")
@given("I have injected single value", target_fixture="foo")
def _():
return "injected foo"
@given("I have injected tuple value", target_fixture="foo")
def _():
return ("injected foo", {"city": ["Boston", "Houston"]}, [10,20,30],)
@then('foo should be "injected foo"')
def _(foo):
assert foo == "injected foo"
@then('foo should be tuple value')
def _(foo):
assert foo == ("injected foo", {"city": ["Boston", "Houston"]}, [10,20,30],)
"""
)
)
result = pytester.runpytest()
result.assert_outcomes(passed=1)


def test_given_injection_multiple(pytester):
def test_given_injection_multiple_values(pytester):
pytester.makefile(
".feature",
given=textwrap.dedent(
"""\
Feature: Given
Scenario: Test given fixture injection
Given I have injecting given
Then foo should be "injected foo"
Given I have injecting given values
Then values should be received
"""
),
)
Expand All @@ -62,18 +70,19 @@ def test_given_injection_multiple(pytester):
def test_given():
pass
@given("I have injecting given", target_fixture="foo,bar")
@given("I have injecting given values", target_fixture="foo,city,numbers")
def _():
return "injected foo", "injected bar"
return ("injected foo", {"city": ["Boston", "Houston"]}, [10,20,30],)
@then('foo should be "injected foo"')
def _(foo, bar):
@then("values should be received")
def _(foo, city, numbers):
assert foo == "injected foo"
assert bar == "injected bar"
assert city == {"city": ["Boston", "Houston"]}
assert numbers == [10,20,30]
"""
)
)
result = pytester.runpytest()
result.assert_outcomes(passed=1)
result.assert_outcomes(passed=1)

0 comments on commit db90668

Please sign in to comment.