Skip to content

Commit 98f6701

Browse files
authored
Merge pull request #431 from pytest-dev/then-when-target-fixture
Support `target_fixture` in `when` and `then` steps.
2 parents 775b6dc + f172bd6 commit 98f6701

File tree

4 files changed

+102
-6
lines changed

4 files changed

+102
-6
lines changed

CHANGES.rst

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Changelog
33

44
Unreleased
55
-----------
6+
- `when` and `then` steps now can provide a `target_fixture`, just like `given` does. Discussion at https://github.com/pytest-dev/pytest-bdd/issues/402.
67
- Drop compatibility for python 2 and officially support only python >= 3.6.
78
- Fix error when using `--cucumber-json-expanded` in combination with `example_converters` (marcbrossaissogeti).
89
- Fix `--generate-missing` not correctly recognizing steps with parsers

README.rst

+41
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,47 @@ it will stay untouched. To allow this, special parameter `target_fixture` exists
359359
In this example existing fixture `foo` will be overridden by given step `I have injecting given` only for scenario it's
360360
used in.
361361

362+
Sometimes it is also useful to let `when` and `then` steps to provide a fixture as well.
363+
A common use case is when we have to assert the outcome of an HTTP request:
364+
365+
.. code-block:: python
366+
367+
# test_blog.py
368+
369+
from pytest_bdd import scenarios, given, when, then
370+
371+
from my_app.models import Article
372+
373+
scenarios("blog.feature")
374+
375+
376+
@given("there is an article", target_fixture="article")
377+
def there_is_an_article():
378+
return Article()
379+
380+
381+
@when("I request the deletion of the article", target_fixture="request_result")
382+
def there_should_be_a_new_article(article, http_client):
383+
return http_client.delete(f"/articles/{article.uid}")
384+
385+
386+
@then("the request should be successful")
387+
def article_is_published(request_result):
388+
assert request_result.status_code == 200
389+
390+
391+
.. code-block:: gherkin
392+
393+
# blog.feature
394+
395+
Feature: Blog
396+
Scenario: Deleting the article
397+
Given there is an article
398+
399+
When I request the deletion of the article
400+
401+
Then the request should be successful
402+
362403
363404
Multiline steps
364405
---------------

pytest_bdd/steps.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -61,35 +61,37 @@ def given(name, converters=None, target_fixture=None):
6161
:param name: Step name or a parser object.
6262
:param converters: Optional `dict` of the argument or parameter converters in form
6363
{<param_name>: <converter function>}.
64-
:param target_fixture: Target fixture name to replace by steps definition function
64+
:param target_fixture: Target fixture name to replace by steps definition function.
6565
6666
:return: Decorator function for the step.
6767
"""
6868
return _step_decorator(GIVEN, name, converters=converters, target_fixture=target_fixture)
6969

7070

71-
def when(name, converters=None):
71+
def when(name, converters=None, target_fixture=None):
7272
"""When step decorator.
7373
7474
:param name: Step name or a parser object.
7575
:param converters: Optional `dict` of the argument or parameter converters in form
7676
{<param_name>: <converter function>}.
77+
:param target_fixture: Target fixture name to replace by steps definition function.
7778
7879
:return: Decorator function for the step.
7980
"""
80-
return _step_decorator(WHEN, name, converters=converters)
81+
return _step_decorator(WHEN, name, converters=converters, target_fixture=target_fixture)
8182

8283

83-
def then(name, converters=None):
84+
def then(name, converters=None, target_fixture=None):
8485
"""Then step decorator.
8586
8687
:param name: Step name or a parser object.
8788
:param converters: Optional `dict` of the argument or parameter converters in form
8889
{<param_name>: <converter function>}.
90+
:param target_fixture: Target fixture name to replace by steps definition function.
8991
9092
:return: Decorator function for the step.
9193
"""
92-
return _step_decorator(THEN, name, converters=converters)
94+
return _step_decorator(THEN, name, converters=converters, target_fixture=target_fixture)
9395

9496

9597
def _step_decorator(step_type, step_name, converters=None, target_fixture=None):

tests/feature/test_steps.py

+53-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import textwrap
2-
import pytest
32

43

54
def test_steps(testdir):
@@ -73,6 +72,59 @@ def check_results(results):
7372
result.assert_outcomes(passed=1, failed=0)
7473

7574

75+
def test_all_steps_can_provide_fixtures(testdir):
76+
"""Test that given/when/then can all provide fixtures."""
77+
testdir.makefile(
78+
".feature",
79+
steps=textwrap.dedent(
80+
"""\
81+
Feature: Step fixture
82+
Scenario: Given steps can provide fixture
83+
Given Foo is "bar"
84+
Then foo should be "bar"
85+
Scenario: When steps can provide fixture
86+
When Foo is "baz"
87+
Then foo should be "baz"
88+
Scenario: Then steps can provide fixture
89+
Then foo is "qux"
90+
And foo should be "qux"
91+
"""
92+
),
93+
)
94+
95+
testdir.makepyfile(
96+
textwrap.dedent(
97+
"""\
98+
from pytest_bdd import given, when, then, parsers, scenarios
99+
100+
scenarios("steps.feature")
101+
102+
@given(parsers.parse('Foo is "{value}"'), target_fixture="foo")
103+
def given_foo_is_value(value):
104+
return value
105+
106+
107+
@when(parsers.parse('Foo is "{value}"'), target_fixture="foo")
108+
def when_foo_is_value(value):
109+
return value
110+
111+
112+
@then(parsers.parse('Foo is "{value}"'), target_fixture="foo")
113+
def then_foo_is_value(value):
114+
return value
115+
116+
117+
@then(parsers.parse('foo should be "{value}"'))
118+
def foo_is_foo(foo, value):
119+
assert foo == value
120+
121+
"""
122+
)
123+
)
124+
result = testdir.runpytest()
125+
result.assert_outcomes(passed=3, failed=0)
126+
127+
76128
def test_when_first(testdir):
77129
testdir.makefile(
78130
".feature",

0 commit comments

Comments
 (0)