From 4e55176e615441fcb9d8d4dddac3e0f5b5d08fec Mon Sep 17 00:00:00 2001 From: Kostiantyn Goloveshko Date: Sat, 1 Oct 2022 13:58:33 +0300 Subject: [PATCH] Update documentation --- CHANGES.rst | 6 ++ README.rst | 185 ++++++++++++++++++++++++---------------------------- 2 files changed, 90 insertions(+), 101 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index d27497ef..13ed048c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,12 @@ Planned - https://github.com/pytest-dev/pytest-bdd/issues/488 - Extra support of official parser - https://github.com/pytest-dev/pytest-bdd/issues/502 +- Rework code generation + +Unreleased +---------- +- Remove possibility to manually register imported steps; They are registered automatically + 1.2.2 ----- diff --git a/README.rst b/README.rst index 852fbaa7..e416f636 100644 --- a/README.rst +++ b/README.rst @@ -2,24 +2,24 @@ BDD library for the pytest runner ================================= .. image:: http://img.shields.io/pypi/v/pytest-bdd-ng.svg - :target: https://pypi.python.org/pypi/pytest-bdd-ng -.. image:: https://codecov.io/gh/elchupanebrej/pytest-bdd/branch/default/graph/badge.svg - :target: https://codecov.io/gh/elchupanebrej/pytest-bdd + :target: https://pypi.python.org/pypi/pytest-bdd-ng +.. image:: https://codecov.io/gh/elchupanebrej/pytest-bdd-ng/branch/default/graph/badge.svg + :target: https://app.codecov.io/gh/elchupanebrej/pytest-bdd-ng .. image:: https://readthedocs.org/projects/pytest-bdd-ng/badge/?version=default :target: https://pytest-bdd-ng.readthedocs.io/en/default/?badge=default :alt: Documentation Status +.. image:: https://badgen.net/badge/stand%20with/UKRAINE/?color=0057B8&labelColor=FFD700 + :target: https://savelife.in.ua/en/ -pytest-bdd implements a subset of the Gherkin language to enable automating project -requirements testing and to facilitate behavioral driven development. - -Unlike many other BDD tools, it does not require a separate runner and benefits from -the power and flexibility of pytest. It enables unifying unit and functional +**pytest-bdd-ng** combine descriptive clarity of `Gherkin `_ language +with power and fullness of `pytest `_ infrastructure +It enables unifying unit and functional tests, reduces the burden of continuous integration server configuration and allows the reuse of test setups. Pytest fixtures written for unit tests can be reused for setup and actions mentioned in feature steps with dependency injection. This allows a true BDD -just-enough specification of the requirements without maintaining any context object +just-enough specification of the requirements without obligatory maintaining any context object containing the side effects of Gherkin imperative declarations. .. _behave: https://pypi.python.org/pypi/behave @@ -33,9 +33,6 @@ Install pytest-bdd-ng pip install pytest-bdd-ng -The minimum required version of `pytest` is 5.0 - - Example ------- @@ -157,29 +154,6 @@ default author. And there's an article -Importing steps ---------------- -Just import steps into test module or `conftest.py` is not enough to be found by pytest-bdd. To use steps from other -module there are few possibilities: - -- Register steps from module: - -.. code-block:: python - - import module_with_steps - - step.from_module(module_with_steps) - -- Import steps and register them from locals: - -.. code-block:: python - - from module_with_steps import given_a, when_b, then_c - - step.from_locals() - -- Build step_registry fixture and register imported steps there - Liberal step decorator ---------------------- Sometimes you want use same step for all types of steps without re-defining alias; @@ -558,7 +532,9 @@ Also it's possible to override multiple fixtures in one step using `target_fixtu Multiline steps --------------- -As Gherkin, pytest-bdd supports multiline steps +**Note!** This possibility not a part of Gherkin standard and is supported for legacy parser. Let use Gherkin docstrings and custom step matcher. + +As Gherkin, pytest-bdd-ng supports multiline steps (aka `PyStrings `_). But in much cleaner and powerful way: @@ -612,14 +588,18 @@ by a `given` step (`i_have_text`) argument with the same name (`text`). This pos the `Step arguments are fixtures as well!`_ section. -Scenarios shortcut ------------------- +Loading whole feature files +--------------------------- If you have relatively large set of feature files, it's boring to manually bind scenarios to the tests using the scenario decorator. Of course with the manual approach you get all the power to be able to additionally parametrize the test, give the test function a nice name, document it, etc, but in the majority of the cases you don't need that. Instead you want to bind `all` scenarios found in the `feature` folder(s) recursively automatically. -For this - there's a `scenarios` helper. + +Scenarios shortcut +^^^^^^^^^^^^^^^^^^ + +First option is `scenarios` helper. .. code-block:: python @@ -670,6 +650,69 @@ Both `scenario` or `scenarios` could be used as decorators or as operator calls. test_specific_scenario = scenario('features/some.feature', 'Test something', return_test_decorator=False) + +Features autoload +^^^^^^^^^^^^^^^^^ + +Another possibility to load features is usage of `--feature-autoload` cli option or `feature_autoload` ini option. +In this case feature files (*.gherkin or *.feature) have to be stored in same structure as origin pytest tests. +Steps from lower directory layers overriding higher ones if step parsing collision occurs. + +Test project layout part could be (pay attention to symlinks): + +:: + + features/ + User login.feature + User creates order.feature + steps/ + user/ + given.py + when.py + then.py + order/ + given.py + when.py + then.py + browser/ + given.py + when.py + then.py + .../ + tests/ + conftest.py + integration/ + conftest.py + User login.feature # -> ../../features/User login.feature + User creates order.feature # -> ../../features/User creates order.feature + ... + unit/ + ... + +tests/conftest.py: + +.. code-block:: python + + from steps.users.given import * + from steps.users.when import * + from steps.users.then import * + + from steps.order.given import * + from steps.order.when import * + from steps.order.then import * + + from steps.browser.given import * + from steps.browser.when import * + from steps.browser.then import * + + +tests/integration/conftest.py: + +.. code-block:: python + + # Other steps specific only for integration scenarios + ... + Scenario outlines ----------------- @@ -752,6 +795,8 @@ different than strings. Feature examples ^^^^^^^^^^^^^^^^ +**Note!** This possibility not a part of Gherkin standard and is supported for legacy parser. + It's possible to declare example table once for the whole feature, and it will be shared among all the scenarios of that feature: @@ -835,68 +880,6 @@ To not repeat steps as in example above you could want store your data in sequen | tomatoes | -Combine scenario outline and pytest parametrization -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -It's also possible to parametrize the scenario on the python side. -The reason for this is that it is sometimes not needed to mention example table for every scenario. - -The code will look like: - -.. code-block:: python - - import pytest - from pytest_bdd import scenario, given, when, then, parsers - - - # Here we use pytest to parametrize the test with the parameters table - @pytest.mark.parametrize( - ["start", "eat", "left"], - [(12, 5, 7)], - ) - @scenario( - "parametrized.feature", - "Parametrized given, when, then", - # Also could be a set, if names of examples and fixtures are same - examples_fixtures_mapping={'start':'start', 'eat':'eat', 'left':'left'} - ) - # 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(parsers.parse("there are {start:d} cucumbers"), target_fixture="start_cucumbers") - def start_cucumbers(start): - return dict(start=start) - - - @when(parsers.parse("I eat {eat:d} cucumbers")) - def eat_cucumbers(start_cucumbers, start, eat): - start_cucumbers["eat"] = eat - - - @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 - assert start_cucumbers["eat"] == eat - - -With a parametrized.feature file: - -.. code-block:: gherkin - - Feature: parametrized - Scenario: Parametrized given, when, then - Given there are cucumbers - When I eat cucumbers - Then I should have cucumbers - - -The significant downside of this approach is inability to see the test table from the feature file. - - Organizing your scenarios -------------------------