Skip to content

Commit 9c6185f

Browse files
pre-commit-ci[bot]arunpkm
authored andcommitted
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent 0959385 commit 9c6185f

File tree

4 files changed

+75
-16
lines changed

4 files changed

+75
-16
lines changed

src/pytest_bdd/scenario.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,17 @@ def _execute_step_function(
162162
raise
163163

164164
if context.target_fixture is not None:
165-
target_fixture_tokens = [token for token in context.target_fixture.split(',') if token]
166-
return_values = (return_value,) if not isinstance(return_value, tuple) else return_value
167-
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)}"
168-
for token, value in zip(target_fixture_tokens, return_values):
169-
inject_fixture(request, token, value)
165+
if isinstance(return_value, Exception):
166+
arg = context.target_exception if context.target_exception else "response"
167+
inject_fixture(request=request, arg=arg, value=return_value)
168+
else:
169+
target_fixture_tokens = [token for token in context.target_fixture.split(",") if token]
170+
return_values = (return_value,) if not isinstance(return_value, tuple) else return_value
171+
assert len(target_fixture_tokens) == len(
172+
return_values
173+
), f"Return value count: {len(return_values)} are not matching target_fixture count: {len(target_fixture_tokens)}"
174+
for token, value in zip(target_fixture_tokens, return_values):
175+
inject_fixture(request=request, arg=token, value=value)
170176

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

src/pytest_bdd/steps.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class StepFunctionContext:
6666
parser: StepParser
6767
converters: dict[str, Callable[..., Any]] = field(default_factory=dict)
6868
target_fixture: str | None = None
69+
target_exception: str | None = None
6970

7071

7172
def get_step_fixture_name(step: Step) -> str:
@@ -96,6 +97,7 @@ def when(
9697
name: str | StepParser,
9798
converters: dict[str, Callable] | None = None,
9899
target_fixture: str | None = None,
100+
target_exception: str | None = None,
99101
stacklevel: int = 1,
100102
) -> Callable:
101103
"""When step decorator.
@@ -104,11 +106,12 @@ def when(
104106
:param converters: Optional `dict` of the argument or parameter converters in form
105107
{<param_name>: <converter function>}.
106108
:param target_fixture: Target fixture name to replace by steps definition function.
109+
:param target_exception: Target exception name to receive Exception object
107110
:param stacklevel: Stack level to find the caller frame. This is used when injecting the step definition fixture.
108111
109112
:return: Decorator function for the step.
110113
"""
111-
return step(name, WHEN, converters=converters, target_fixture=target_fixture, stacklevel=stacklevel)
114+
return step(name, WHEN, converters=converters, target_fixture=target_fixture, target_exception=target_exception, stacklevel=stacklevel)
112115

113116

114117
def then(
@@ -135,6 +138,7 @@ def step(
135138
type_: Literal["given", "when", "then"] | None = None,
136139
converters: dict[str, Callable] | None = None,
137140
target_fixture: str | None = None,
141+
target_exception: str | None = None,
138142
stacklevel: int = 1,
139143
) -> Callable[[TCallable], TCallable]:
140144
"""Generic step decorator.
@@ -143,6 +147,7 @@ def step(
143147
:param type_: Step type ("given", "when" or "then"). If None, this step will work for all the types.
144148
:param converters: Optional step arguments converters mapping.
145149
:param target_fixture: Optional fixture name to replace by step definition.
150+
:param target_exception: Optional target exception name
146151
:param stacklevel: Stack level to find the caller frame. This is used when injecting the step definition fixture.
147152
148153
:return: Decorator function for the step.
@@ -165,6 +170,7 @@ def decorator(func: TCallable) -> TCallable:
165170
parser=parser,
166171
converters=converters,
167172
target_fixture=target_fixture,
173+
target_exception=target_exception,
168174
)
169175

170176
def step_function_marker() -> StepFunctionContext:

tests/feature/test_steps.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,3 +584,49 @@ def _(stuff):
584584
"*Tearing down...*",
585585
]
586586
)
587+
588+
589+
def test_when_exception(pytester):
590+
pytester.makefile(
591+
".feature",
592+
when_exception=textwrap.dedent(
593+
"""\
594+
Feature: When exception
595+
Scenario: Test when exception is generated
596+
When I have injected exception
597+
Then Exception object should be received as default 'response' param
598+
When I have injected exception with target_exception='exception_ret'
599+
Then Exception object should be received as target_exception='exception_ret'
600+
"""
601+
),
602+
)
603+
pytester.makepyfile(
604+
textwrap.dedent(
605+
"""\
606+
import pytest
607+
from pytest_bdd import when, then, scenario
608+
609+
@scenario("when_exception.feature", "Test when exception is generated")
610+
def test_when_exception():
611+
pass
612+
613+
@when("I have injected exception", target_fixture="foo")
614+
def _():
615+
return Exception("Dummy Exception obj")
616+
617+
@when("I have injected exception with target_exception='exception_ret'", target_fixture="foo", target_exception="exception_ret")
618+
def _():
619+
return Exception("Dummy Exception obj")
620+
621+
@then("Exception object should be received as default 'response' param")
622+
def _(response):
623+
assert isinstance(response, Exception), "response is not Exception object"
624+
625+
@then("Exception object should be received as target_exception='exception_ret'")
626+
def _(exception_ret):
627+
assert isinstance(exception_ret, Exception), "response is not Exception object"
628+
"""
629+
)
630+
)
631+
result = pytester.runpytest()
632+
result.assert_outcomes(passed=1)

tests/steps/test_given.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import textwrap
33

44

5-
def test_given_injection(pytester):
5+
def test_given_injection_single_value(pytester):
66
pytester.makefile(
77
".feature",
88
given=textwrap.dedent(
@@ -40,15 +40,15 @@ def _(foo):
4040
result.assert_outcomes(passed=1)
4141

4242

43-
def test_given_injection_multiple(pytester):
43+
def test_given_injection_multiple_values(pytester):
4444
pytester.makefile(
4545
".feature",
4646
given=textwrap.dedent(
4747
"""\
4848
Feature: Given
4949
Scenario: Test given fixture injection
50-
Given I have injecting given
51-
Then foo should be "injected foo"
50+
Given I have injecting given values
51+
Then values should be received
5252
"""
5353
),
5454
)
@@ -62,18 +62,19 @@ def test_given_injection_multiple(pytester):
6262
def test_given():
6363
pass
6464
65-
@given("I have injecting given", target_fixture="foo,bar")
65+
@given("I have injecting given values", target_fixture="foo,city,numbers")
6666
def _():
67-
return "injected foo", "injected bar"
67+
return ("injected foo", {"city": ["Boston", "Houston"]}, [10,20,30],)
6868
6969
70-
@then('foo should be "injected foo"')
71-
def _(foo, bar):
70+
@then("values should be received")
71+
def _(foo, city, numbers):
7272
assert foo == "injected foo"
73-
assert bar == "injected bar"
73+
assert city == {"city": ["Boston", "Houston"]}
74+
assert numbers == [10,20,30]
7475
7576
"""
7677
)
7778
)
7879
result = pytester.runpytest()
79-
result.assert_outcomes(passed=1)
80+
result.assert_outcomes(passed=1)

0 commit comments

Comments
 (0)