Skip to content

Commit db90668

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 db90668

File tree

4 files changed

+90
-18
lines changed

4 files changed

+90
-18
lines changed

src/pytest_bdd/scenario.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,22 @@ 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+
# Single return value in target_fixture
171+
if len(target_fixture_tokens) == 1:
172+
inject_fixture(request=request, arg=target_fixture_tokens[0], value=return_value)
173+
# Multiple comma separated return values in target_fixture
174+
else:
175+
return_values = (return_value,) if not isinstance(return_value, tuple) else return_value
176+
assert len(target_fixture_tokens) == len(
177+
return_values
178+
), f"Return value count: {len(return_values)} are not matching target_fixture count: {len(target_fixture_tokens)}"
179+
for token, value in zip(target_fixture_tokens, return_values):
180+
inject_fixture(request=request, arg=token, value=value)
170181

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

src/pytest_bdd/steps.py

+7-1
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

+46
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

+21-12
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
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(
99
"""\
1010
Feature: Given
1111
Scenario: Test given fixture injection
12-
Given I have injecting given
12+
Given I have injected single value
1313
Then foo should be "injected foo"
14+
Given I have injected tuple value
15+
Then foo should be tuple value
1416
"""
1517
),
1618
)
@@ -24,31 +26,37 @@ def test_given_injection(pytester):
2426
def test_given():
2527
pass
2628
27-
@given("I have injecting given", target_fixture="foo")
29+
@given("I have injected single value", target_fixture="foo")
2830
def _():
2931
return "injected foo"
3032
33+
@given("I have injected tuple value", target_fixture="foo")
34+
def _():
35+
return ("injected foo", {"city": ["Boston", "Houston"]}, [10,20,30],)
3136
3237
@then('foo should be "injected foo"')
3338
def _(foo):
3439
assert foo == "injected foo"
3540
41+
@then('foo should be tuple value')
42+
def _(foo):
43+
assert foo == ("injected foo", {"city": ["Boston", "Houston"]}, [10,20,30],)
3644
"""
3745
)
3846
)
3947
result = pytester.runpytest()
4048
result.assert_outcomes(passed=1)
4149

4250

43-
def test_given_injection_multiple(pytester):
51+
def test_given_injection_multiple_values(pytester):
4452
pytester.makefile(
4553
".feature",
4654
given=textwrap.dedent(
4755
"""\
4856
Feature: Given
4957
Scenario: Test given fixture injection
50-
Given I have injecting given
51-
Then foo should be "injected foo"
58+
Given I have injecting given values
59+
Then values should be received
5260
"""
5361
),
5462
)
@@ -62,18 +70,19 @@ def test_given_injection_multiple(pytester):
6270
def test_given():
6371
pass
6472
65-
@given("I have injecting given", target_fixture="foo,bar")
73+
@given("I have injecting given values", target_fixture="foo,city,numbers")
6674
def _():
67-
return "injected foo", "injected bar"
75+
return ("injected foo", {"city": ["Boston", "Houston"]}, [10,20,30],)
6876
6977
70-
@then('foo should be "injected foo"')
71-
def _(foo, bar):
78+
@then("values should be received")
79+
def _(foo, city, numbers):
7280
assert foo == "injected foo"
73-
assert bar == "injected bar"
81+
assert city == {"city": ["Boston", "Houston"]}
82+
assert numbers == [10,20,30]
7483
7584
"""
7685
)
7786
)
7887
result = pytester.runpytest()
79-
result.assert_outcomes(passed=1)
88+
result.assert_outcomes(passed=1)

0 commit comments

Comments
 (0)