Skip to content

Commit

Permalink
Merge pull request #431 from pytest-dev/then-when-target-fixture
Browse files Browse the repository at this point in the history
Support `target_fixture` in `when` and `then` steps.
  • Loading branch information
youtux authored Jul 3, 2021
2 parents 775b6dc + f172bd6 commit 98f6701
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Changelog

Unreleased
-----------
- `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.
- Drop compatibility for python 2 and officially support only python >= 3.6.
- Fix error when using `--cucumber-json-expanded` in combination with `example_converters` (marcbrossaissogeti).
- Fix `--generate-missing` not correctly recognizing steps with parsers
Expand Down
41 changes: 41 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,47 @@ it will stay untouched. To allow this, special parameter `target_fixture` exists
In this example existing fixture `foo` will be overridden by given step `I have injecting given` only for scenario it's
used in.

Sometimes it is also useful to let `when` and `then` steps to provide a fixture as well.
A common use case is when we have to assert the outcome of an HTTP request:

.. code-block:: python
# test_blog.py
from pytest_bdd import scenarios, given, when, then
from my_app.models import Article
scenarios("blog.feature")
@given("there is an article", target_fixture="article")
def there_is_an_article():
return Article()
@when("I request the deletion of the article", target_fixture="request_result")
def there_should_be_a_new_article(article, http_client):
return http_client.delete(f"/articles/{article.uid}")
@then("the request should be successful")
def article_is_published(request_result):
assert request_result.status_code == 200
.. code-block:: gherkin
# blog.feature
Feature: Blog
Scenario: Deleting the article
Given there is an article
When I request the deletion of the article
Then the request should be successful
Multiline steps
---------------
Expand Down
12 changes: 7 additions & 5 deletions pytest_bdd/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,35 +61,37 @@ def given(name, converters=None, target_fixture=None):
: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
: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):
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)
return _step_decorator(WHEN, name, converters=converters, target_fixture=target_fixture)


def then(name, converters=None):
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)
return _step_decorator(THEN, name, converters=converters, target_fixture=target_fixture)


def _step_decorator(step_type, step_name, converters=None, target_fixture=None):
Expand Down
54 changes: 53 additions & 1 deletion tests/feature/test_steps.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import textwrap
import pytest


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


def test_all_steps_can_provide_fixtures(testdir):
"""Test that given/when/then can all provide fixtures."""
testdir.makefile(
".feature",
steps=textwrap.dedent(
"""\
Feature: Step fixture
Scenario: Given steps can provide fixture
Given Foo is "bar"
Then foo should be "bar"
Scenario: When steps can provide fixture
When Foo is "baz"
Then foo should be "baz"
Scenario: Then steps can provide fixture
Then foo is "qux"
And foo should be "qux"
"""
),
)

testdir.makepyfile(
textwrap.dedent(
"""\
from pytest_bdd import given, when, then, parsers, scenarios
scenarios("steps.feature")
@given(parsers.parse('Foo is "{value}"'), target_fixture="foo")
def given_foo_is_value(value):
return value
@when(parsers.parse('Foo is "{value}"'), target_fixture="foo")
def when_foo_is_value(value):
return value
@then(parsers.parse('Foo is "{value}"'), target_fixture="foo")
def then_foo_is_value(value):
return value
@then(parsers.parse('foo should be "{value}"'))
def foo_is_foo(foo, value):
assert foo == value
"""
)
)
result = testdir.runpytest()
result.assert_outcomes(passed=3, failed=0)


def test_when_first(testdir):
testdir.makefile(
".feature",
Expand Down

0 comments on commit 98f6701

Please sign in to comment.