Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite scenario/feature examples logic #445

Merged
merged 64 commits into from
Sep 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
971528a
Very bare implementation of examples subsitution using templating, in…
youtux Aug 21, 2021
8c0db0f
Start removing example_converters
youtux Aug 22, 2021
54ac821
Make sure that `_pytest_bdd_example` is always not empty, otherwise t…
youtux Aug 22, 2021
5c84d05
Fix test
youtux Aug 22, 2021
58bc825
Remove occurrences of example_converters
youtux Aug 22, 2021
34e3da1
Fix background steps not being used
youtux Aug 22, 2021
aa2ef25
readme updated
olegpidsadnyi Aug 22, 2021
273c594
Fix test
youtux Aug 22, 2021
13ce955
Cleanup code
youtux Aug 22, 2021
464ef40
Fix indirect fixtures not working
youtux Aug 22, 2021
af6e4a2
Fix test
youtux Aug 22, 2021
078fe8f
Fix test
youtux Aug 22, 2021
3bc2397
Remove "expanded" options. It's now the default.
youtux Aug 22, 2021
c81e06d
Minor trivial fixes
youtux Aug 22, 2021
7190286
Minor trivial fixes
youtux Aug 22, 2021
41b8627
Simplify code
youtux Aug 22, 2021
b6e442c
Remove unused argument
youtux Aug 22, 2021
4ba901a
PR comments
olegpidsadnyi Aug 22, 2021
5698c5c
Adding a comment that example values are no longer passed as fixtures
olegpidsadnyi Aug 23, 2021
e7f9b93
Merge branch 'examples-templating' into examples-templating-readme
youtux Aug 25, 2021
b07634a
Merge pull request #441 from pytest-dev/examples-templating-readme
youtux Aug 25, 2021
86aa214
Fix bug when empty examples in Feature results in test being skipped.
youtux Aug 29, 2021
aecf780
Merge remote-tracking branch 'origin/examples-templating' into exampl…
youtux Aug 29, 2021
5ce621d
fix no value being yielded
youtux Aug 29, 2021
67d1868
Fix iterator being consumed on the first time
youtux Aug 29, 2021
6acd804
Fix test
youtux Aug 29, 2021
1ee3b4b
Fix test, but I'm not sure what we want to do with this behaviour.
youtux Aug 29, 2021
5a97833
Add utility functions to be able to inspect tests run by the pytester.
youtux Sep 16, 2021
660bac0
Improve test inspection
youtux Sep 16, 2021
d930d51
Remove unused import, use better timer
youtux Sep 16, 2021
98a224b
Fix typos
youtux Sep 16, 2021
90ad0fa
Fix and simplify tests
youtux Sep 18, 2021
60285ad
Remove unused code
youtux Sep 18, 2021
9010a16
Fix test items ending with "[]" (for example, "test_scenarios.py::tes…
youtux Sep 18, 2021
4946589
Fix tests
youtux Sep 18, 2021
c7c04f9
Fix test
youtux Sep 18, 2021
9eb8511
Update to latest python 3.10 version
youtux Sep 18, 2021
d6dacc9
Fix pytest < 6.2 compatibility
youtux Sep 18, 2021
6783783
Fix readme
youtux Sep 18, 2021
68cd139
use f-string
youtux Sep 18, 2021
7ed5bf6
Inline method get_params()
youtux Sep 18, 2021
e7b8ecb
Move re to the beginning of the module
youtux Sep 18, 2021
e638007
Update TODO
youtux Sep 18, 2021
0e4d271
Remove unused attribute
youtux Sep 18, 2021
dfe124b
Sort imports
youtux Sep 18, 2021
a1c92bd
Add isort configuration and pre-commit hook
youtux Sep 18, 2021
bf85037
Fix imports
youtux Sep 18, 2021
89ef9e0
remove copy-pasted code
youtux Sep 19, 2021
e6aefa5
Fix types
youtux Sep 19, 2021
764bf79
Fix TODOs
youtux Sep 19, 2021
918fecf
Simplify test
youtux Sep 19, 2021
e0ce7e4
FIx typing
youtux Sep 19, 2021
58cf81d
Remove TODO
youtux Sep 19, 2021
a4c27cf
Update changelog
youtux Sep 19, 2021
99420f2
Update Readme
youtux Sep 19, 2021
9383530
Update README (mainly fix typos, remove outdated options)
youtux Sep 19, 2021
b817fbf
Fix examples in README
youtux Sep 19, 2021
e2eadb5
Remove python2 junk
youtux Sep 19, 2021
030937f
Fix migration guide
youtux Sep 19, 2021
02ce717
Fix migration guide
youtux Sep 19, 2021
2710c5a
Improve test assertion
youtux Sep 19, 2021
d5df79e
Simplify test
youtux Sep 19, 2021
3fe45a8
Fix test to reflect how it was before
youtux Sep 19, 2021
28f3261
use converters
youtux Sep 19, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", 3.10.0-beta.4]
python-version: ["3.6", "3.7", "3.8", "3.9", 3.10.0-rc.2]

steps:
- uses: actions/checkout@v2
Expand Down
9 changes: 7 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/psf/black
rev: 21.6b0
rev: 21.9b0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.9.3
hooks:
- id: isort
name: isort (python)
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
hooks:
Expand All @@ -13,7 +18,7 @@ repos:
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/asottile/pyupgrade
rev: v2.19.4
rev: v2.26.0
hooks:
- id: pyupgrade
args: [--py36-plus]
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ Changelog

Unreleased
-----------
This release introduces breaking changes, please refer to the :ref:`Migration from 4.x.x`.

- Rewrite the logic to parse Examples for Scenario Outlines. Now the substitution of the examples is done during the parsing of Gherkin feature files. You won't need to define the steps twice like ``@given("there are <start> cucumbers")`` and ``@given(parsers.parse("there are {start} cucumbers"))``. The latter will be enough.
- Removed ``example_converters`` from ``scenario(...)`` signature. You should now use just the ``converters`` parameter for ``given``, ``when``, ``then``.
- Removed ``--cucumberjson-expanded`` and ``--cucumber-json-expanded`` options. Now the JSON report is always expanded.
- Removed ``--gherkin-terminal-reporter-expanded`` option. Now the terminal report is always expanded.

4.1.0
-----------
Expand Down
144 changes: 77 additions & 67 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,8 @@ test_publish_article.py:
Scenario decorator
------------------

The scenario decorator can accept the following optional keyword arguments:

* ``encoding`` - decode content of feature file in specific encoding. UTF-8 is default.
* ``example_converters`` - mapping to pass functions to convert example values provided in feature files.

Functions decorated with the `scenario` decorator behave like a normal test function,
and they will be executed after all scenario steps.
You can consider it as a normal pytest test function, e.g. order fixtures there,
call other functions and make assertions:


.. code-block:: python
Expand All @@ -129,6 +122,9 @@ call other functions and make assertions:
assert article.title in browser.html


.. NOTE:: It is however encouraged to try as much as possible to have your logic only inside the Given, When, Then steps.


Step aliases
------------

Expand Down Expand Up @@ -239,7 +235,7 @@ Example:
.. code-block:: gherkin

Feature: Step arguments
Scenario: Arguments for given, when, thens
Scenario: Arguments for given, when, then
Given there are 5 cucumbers

When I eat 3 cucumbers
Expand All @@ -256,7 +252,7 @@ The code will look like:
from pytest_bdd import scenario, given, when, then, parsers


@scenario("arguments.feature", "Arguments for given, when, thens")
@scenario("arguments.feature", "Arguments for given, when, then")
def test_arguments():
pass

Expand Down Expand Up @@ -292,7 +288,7 @@ You can implement your own step parser. It's interface is quite simple. The code

def __init__(self, name, **kwargs):
"""Compile regex."""
super(re, self).__init__(name)
super().__init__(name)
self.regex = re.compile(re.sub("%(.+)%", "(?P<\1>.+)", self.name), **kwargs)

def parse_arguments(self, name):
Expand All @@ -316,9 +312,9 @@ Step arguments are fixtures as well!
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Step arguments are injected into pytest `request` context as normal fixtures with the names equal to the names of the
arguments. This opens a number of possibilies:
arguments. This opens a number of possibilities:

* you can access step's argument as a fixture in other step function just by mentioning it as an argument (just like any othe pytest fixture)
* you can access step's argument as a fixture in other step function just by mentioning it as an argument (just like any other pytest fixture)
* if the name of the step argument clashes with existing fixture, it will be overridden by step's argument value; this way you can set/override the value for some fixture deeply inside of the fixture tree in a ad-hoc way by just choosing the proper name for the step argument.


Expand Down Expand Up @@ -433,7 +429,7 @@ step arguments and capture lines after first line (or some subset of them) into

import re

from pytest_bdd import given, then, scenario
from pytest_bdd import given, then, scenario, parsers


@scenario(
Expand All @@ -454,7 +450,7 @@ step arguments and capture lines after first line (or some subset of them) into
assert i_have_text == text == 'Some\nExtra\nLines'

Note that `then` step definition (`text_should_be_correct`) in this example uses `text` fixture which is provided
by a a `given` step (`i_have_text`) argument with the same name (`text`). This possibility is described in
by a `given` step (`i_have_text`) argument with the same name (`text`). This possibility is described in
the `Step arguments are fixtures as well!`_ section.


Expand Down Expand Up @@ -508,7 +504,7 @@ Scenario outlines
-----------------

Scenarios can be parametrized to cover few cases. In Gherkin the variable
templates are written using corner braces as <somevalue>.
templates are written using corner braces as ``<somevalue>``.
`Gherkin scenario outlines <http://behat.org/en/v3.0/user_guide/writing_scenarios.html#scenario-outlines>`_ are supported by pytest-bdd
exactly as it's described in be behave_ docs.

Expand All @@ -517,7 +513,7 @@ Example:
.. code-block:: gherkin

Feature: Scenario outlines
Scenario Outline: Outlined given, when, thens
Scenario Outline: Outlined given, when, then
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers
Expand All @@ -532,7 +528,7 @@ pytest-bdd feature file format also supports example tables in different way:
.. code-block:: gherkin

Feature: Scenario outlines
Scenario Outline: Outlined given, when, thens
Scenario Outline: Outlined given, when, then
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers
Expand All @@ -549,31 +545,30 @@ The code will look like:

.. code-block:: python

from pytest_bdd import given, when, then, scenario
from pytest_bdd import given, when, then, scenario, parsers


@scenario(
"outline.feature",
"Outlined given, when, thens",
example_converters=dict(start=int, eat=float, left=str)
"Outlined given, when, then",
)
def test_outlined():
pass


@given("there are <start> cucumbers", target_fixture="start_cucumbers")
@given(parsers.parse("there are {start:d} cucumbers", target_fixture="start_cucumbers"))
def start_cucumbers(start):
assert isinstance(start, int)
return dict(start=start)


@when("I eat <eat> cucumbers")
@when(parsers.parse("I eat {eat:g} cucumbers"))
def eat_cucumbers(start_cucumbers, eat):
assert isinstance(eat, float)
start_cucumbers["eat"] = eat


@then("I should have <left> cucumbers")
@then(parsers.parse("I should have {left} cucumbers"))
def should_have_left_cucumbers(start_cucumbers, start, eat, left):
assert isinstance(left, str)
assert start - eat == int(left)
Expand Down Expand Up @@ -654,7 +649,7 @@ The code will look like:
.. code-block:: python

import pytest
from pytest_bdd import scenario, given, when, then
from pytest_bdd import scenario, given, when, then, parsers


# Here we use pytest to parametrize the test with the parameters table
Expand All @@ -664,25 +659,25 @@ The code will look like:
)
@scenario(
"parametrized.feature",
"Parametrized given, when, thens",
"Parametrized given, when, then",
)
# Note that we should take the same arguments in the test function that we use
# for the test parametrization either directly or indirectly (fixtures depend on them).
def test_parametrized(start, eat, left):
"""We don't need to do anything here, everything will be managed by the scenario decorator."""


@given("there are <start> cucumbers", target_fixture="start_cucumbers")
@given(parsers.parse("there are {start:d} cucumbers"), target_fixture="start_cucumbers")
def start_cucumbers(start):
return dict(start=start)


@when("I eat <eat> cucumbers")
@when(parsers.parse("I eat {eat:d} cucumbers"))
def eat_cucumbers(start_cucumbers, start, eat):
start_cucumbers["eat"] = eat


@then("I should have <left> cucumbers")
@then(parsers.parse("I should have {left:d} cucumbers"))
def should_have_left_cucumbers(start_cucumbers, start, eat, left):
assert start - eat == left
assert start_cucumbers["start"] == start
Expand All @@ -694,7 +689,7 @@ With a parametrized.feature file:
.. code-block:: gherkin

Feature: parametrized
Scenario: Parametrized given, when, thens
Scenario: Parametrized given, when, then
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers
Expand Down Expand Up @@ -773,12 +768,12 @@ scenario test, so we can use standard test selection:

pytest -m "backend and login and successful"

The feature and scenario markers are not different from standard pytest markers, and the `@` symbol is stripped out
The feature and scenario markers are not different from standard pytest markers, and the ``@`` symbol is stripped out
automatically to allow test selector expressions. If you want to have bdd-related tags to be distinguishable from the
other test markers, use prefix like `bdd`.
Note that if you use pytest `--strict` option, all bdd tags mentioned in the feature files should be also in the
`markers` setting of the `pytest.ini` config. Also for tags please use names which are python-compartible variable
names, eg starts with a non-number, underscore alphanumberic, etc. That way you can safely use tags for tests filtering.
`markers` setting of the `pytest.ini` config. Also for tags please use names which are python-compatible variable
names, eg starts with a non-number, underscore alphanumeric, etc. That way you can safely use tags for tests filtering.

You can customize how tags are converted to pytest marks by implementing the
``pytest_bdd_apply_tag`` hook and returning ``True`` from it:
Expand All @@ -791,7 +786,7 @@ You can customize how tags are converted to pytest marks by implementing the
marker(function)
return True
else:
# Fall back to pytest-bdd's default behavior
# Fall back to the default behavior of pytest-bdd
return None

Test setup
Expand Down Expand Up @@ -978,23 +973,7 @@ test_common.py:
pass

There are no definitions of the steps in the test file. They were
collected from the parent conftests.


Using unicode in the feature files
----------------------------------

As mentioned above, by default, utf-8 encoding is used for parsing feature files.
For steps definition, you should use unicode strings, which is the default in python 3.
If you are on python 2, make sure you use unicode strings by prefixing them with the `u` sign.


.. code-block:: python

@given(parsers.re(u"у мене є рядок який містить '{0}'".format(u'(?P<content>.+)')))
def there_is_a_string_with_content(content, string):
"""Create string with unicode content."""
string["content"] = content
collected from the parent conftest.py.


Default steps
Expand Down Expand Up @@ -1050,7 +1029,7 @@ The `features_base_dir` parameter can also be passed to the `@scenario` decorato
Avoid retyping the feature file name
------------------------------------

If you want to avoid retyping the feature file name when defining your scenarios in a test file, use functools.partial.
If you want to avoid retyping the feature file name when defining your scenarios in a test file, use ``functools.partial``.
This will make your life much easier when defining multiple scenarios in a test file. For example:

test_publish_article.py:
Expand Down Expand Up @@ -1118,8 +1097,8 @@ Reporting

It's important to have nice reporting out of your bdd tests. Cucumber introduced some kind of standard for
`json format <https://www.relishapp.com/cucumber/cucumber/docs/json-output-formatter>`_
which can be used for `this <https://wiki.jenkins-ci.org/display/JENKINS/Cucumber+Test+Result+Plugin>`_ jenkins
plugin
which can be used for, for example, by `this <https://plugins.jenkins.io/cucumber-testresult-plugin/>`_ Jenkins
plugin.

To have an output in json format:

Expand All @@ -1128,11 +1107,6 @@ To have an output in json format:
pytest --cucumberjson=<path to json report>

This will output an expanded (meaning scenario outlines will be expanded to several scenarios) cucumber format.
To also fill in parameters in the step name, you have to explicitly tell pytest-bdd to use the expanded format:

::

pytest --cucumberjson=<path to json report> --cucumberjson-expanded

To enable gherkin-formatted output on terminal, use

Expand All @@ -1141,14 +1115,6 @@ To enable gherkin-formatted output on terminal, use
pytest --gherkin-terminal-reporter


Terminal reporter supports expanded format as well

::

pytest --gherkin-terminal-reporter-expanded



Test code generation helpers
----------------------------

Expand Down Expand Up @@ -1208,6 +1174,51 @@ As as side effect, the tool will validate the files for format errors, also some
ordering of the types of the steps.


.. _Migration from 4.x.x:

Migration of your tests from versions 4.x.x
-------------------------------------------

Templated steps (e.g. ``@given("there are <start> cucumbers")``) should now the use step argument parsers in order to match the scenario outlines and get the values from the example tables. The values from the example tables are no longer passed as fixtures, although if you define your step to use a parser, the parameters will be still provided as fixtures.

.. code-block:: python

# Old step definition:
@given("there are <start> cucumbers")
def given_cucumbers(start):
pass


# New step definition:
@given(parsers.parse("there are {start} cucumbers"))
def given_cucumbers(start):
pass


Scenario `example_converters` are removed in favor of the converters provided on the step level:

.. code-block:: python

# Old code:
@given("there are <start> cucumbers")
def given_cucumbers(start):
return {"start": start}

@scenario("outline.feature", "Outlined", example_converters={"start": float})
def test_outline():
pass


# New code:
@given(parsers.parse("there are {start} cucumbers"), converters={"start": float})
def given_cucumbers(start):
return {"start": start}

@scenario("outline.feature", "Outlined")
def test_outline():
pass


.. _Migration from 3.x.x:

Migration of your tests from versions 3.x.x
Expand Down Expand Up @@ -1240,7 +1251,6 @@ as well as ``bdd_strict_gherkin`` from the ini files.

Step validation handlers for the hook ``pytest_bdd_step_validation_error`` should be removed.


License
-------

Expand Down
Loading