From ad593d2caf76ef330551dbf26b736c0f79fb9b7c Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sat, 3 Jul 2021 09:45:21 +0200 Subject: [PATCH 1/4] Let "when" and "then" steps define a target fixture. See discussion at https://github.com/pytest-dev/pytest-bdd/issues/402#issuecomment-869615840 --- pytest_bdd/steps.py | 12 +++++---- tests/feature/test_steps.py | 54 ++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/pytest_bdd/steps.py b/pytest_bdd/steps.py index fb038a5..df6e954 100644 --- a/pytest_bdd/steps.py +++ b/pytest_bdd/steps.py @@ -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 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 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 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): diff --git a/tests/feature/test_steps.py b/tests/feature/test_steps.py index 27c4409..63ed149 100644 --- a/tests/feature/test_steps.py +++ b/tests/feature/test_steps.py @@ -1,5 +1,4 @@ import textwrap -import pytest def test_steps(testdir): @@ -73,6 +72,59 @@ def test_steps(testdir): 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", From c3263c6abf876bf2d3720b5cb05f638b324e9e19 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sat, 3 Jul 2021 10:01:04 +0200 Subject: [PATCH 2/4] Add documentation about `when`, `then` steps supporting target_fixture --- README.rst | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/README.rst b/README.rst index eb739d6..e49391d 100644 --- a/README.rst +++ b/README.rst @@ -359,6 +359,43 @@ 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 + + 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): # <-- Important bit here + 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 --------------- From 82c930f714c4316dc745c390504c108938a1ef13 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sat, 3 Jul 2021 10:02:33 +0200 Subject: [PATCH 3/4] Add changelog entry --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index ec1a2d6..1636bd1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -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 From 2716593c47b28e4bef275fc05586f14ce18038f9 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sat, 3 Jul 2021 10:05:27 +0200 Subject: [PATCH 4/4] Fix example --- README.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index e49391d..5329a64 100644 --- a/README.rst +++ b/README.rst @@ -368,20 +368,24 @@ A common use case is when we have to assert the outcome of an HTTP request: 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): # <-- Important bit here + 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 - + assert request_result.status_code == 200 .. code-block:: gherkin