Merge pull request #534 from pytest-dev/reusable-step-functions
Reusable step functions
This commit is contained in:
commit
e24aee0028
|
@ -1,6 +1,11 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
Unreleased
|
||||||
|
----------
|
||||||
|
- Fix bug where steps without parsers would take precedence over steps with parsers. `#534 <https://github.com/pytest-dev/pytest-bdd/pull/534>`_
|
||||||
|
- Step functions can now be decorated multiple times with @given, @when, @then. Previously every decorator would override ``converters`` and ``target_fixture`` every at every application. `#534 <https://github.com/pytest-dev/pytest-bdd/pull/534>`_ `#525 <https://github.com/pytest-dev/pytest-bdd/issues/525>`_
|
||||||
|
|
||||||
6.0.1
|
6.0.1
|
||||||
-----
|
-----
|
||||||
- Fix regression introduced in 6.0.0 where a step function decorated multiple using a parsers times would not be executed correctly. `#530 <https://github.com/pytest-dev/pytest-bdd/pull/530>`_ `#528 <https://github.com/pytest-dev/pytest-bdd/issues/528>`_
|
- Fix regression introduced in 6.0.0 where a step function decorated multiple using a parsers times would not be executed correctly. `#530 <https://github.com/pytest-dev/pytest-bdd/pull/530>`_ `#528 <https://github.com/pytest-dev/pytest-bdd/issues/528>`_
|
||||||
|
|
11
README.rst
11
README.rst
|
@ -208,12 +208,11 @@ for `cfparse` parser
|
||||||
from pytest_bdd import parsers
|
from pytest_bdd import parsers
|
||||||
|
|
||||||
@given(
|
@given(
|
||||||
parsers.cfparse("there are {start:Number} cucumbers",
|
parsers.cfparse("there are {start:Number} cucumbers", extra_types={"Number": int}),
|
||||||
extra_types=dict(Number=int)),
|
|
||||||
target_fixture="cucumbers",
|
target_fixture="cucumbers",
|
||||||
)
|
)
|
||||||
def given_cucumbers(start):
|
def given_cucumbers(start):
|
||||||
return dict(start=start, eat=0)
|
return {"start": start, "eat": 0}
|
||||||
|
|
||||||
for `re` parser
|
for `re` parser
|
||||||
|
|
||||||
|
@ -223,11 +222,11 @@ for `re` parser
|
||||||
|
|
||||||
@given(
|
@given(
|
||||||
parsers.re(r"there are (?P<start>\d+) cucumbers"),
|
parsers.re(r"there are (?P<start>\d+) cucumbers"),
|
||||||
converters=dict(start=int),
|
converters={"start": int},
|
||||||
target_fixture="cucumbers",
|
target_fixture="cucumbers",
|
||||||
)
|
)
|
||||||
def given_cucumbers(start):
|
def given_cucumbers(start):
|
||||||
return dict(start=start, eat=0)
|
return {"start": start, "eat": 0}
|
||||||
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
@ -301,7 +300,7 @@ You can implement your own step parser. It's interface is quite simple. The code
|
||||||
|
|
||||||
@given(parsers.parse("there are %start% cucumbers"), target_fixture="cucumbers")
|
@given(parsers.parse("there are %start% cucumbers"), target_fixture="cucumbers")
|
||||||
def given_cucumbers(start):
|
def given_cucumbers(start):
|
||||||
return dict(start=start, eat=0)
|
return {"start": start, "eat": 0}
|
||||||
|
|
||||||
|
|
||||||
Override fixtures via given steps
|
Override fixtures via given steps
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomicwrites"
|
name = "atomicwrites"
|
||||||
version = "1.4.0"
|
version = "1.4.1"
|
||||||
description = "Atomic file writes."
|
description = "Atomic file writes."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -36,6 +36,17 @@ category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "execnet"
|
||||||
|
version = "1.9.0"
|
||||||
|
description = "execnet: rapid multi-Python deployment"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
testing = ["pre-commit"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filelock"
|
name = "filelock"
|
||||||
version = "3.7.1"
|
version = "3.7.1"
|
||||||
|
@ -236,6 +247,36 @@ tomli = ">=1.0.0"
|
||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest-forked"
|
||||||
|
version = "1.4.0"
|
||||||
|
description = "run tests in isolated forked subprocesses"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
py = "*"
|
||||||
|
pytest = ">=3.10"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest-xdist"
|
||||||
|
version = "2.5.0"
|
||||||
|
description = "pytest xdist plugin for distributed testing and loop-on-failing modes"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
execnet = ">=1.1"
|
||||||
|
pytest = ">=6.2.0"
|
||||||
|
pytest-forked = "*"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
psutil = ["psutil (>=3.0)"]
|
||||||
|
setproctitle = ["setproctitle"]
|
||||||
|
testing = ["filelock"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
|
@ -341,12 +382,11 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7"
|
python-versions = "^3.7"
|
||||||
content-hash = "ecb8acab4af64ddf36f5510f9d99cf462b03d9bdbd18042041dc4d374a44a7ba"
|
content-hash = "a7818e3872ac60220902d85673d411ecfd067f8ec89a6099e4f720c5925e535a"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
atomicwrites = [
|
atomicwrites = [
|
||||||
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
{file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
|
||||||
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
|
|
||||||
]
|
]
|
||||||
attrs = [
|
attrs = [
|
||||||
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
||||||
|
@ -356,12 +396,21 @@ colorama = [
|
||||||
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
|
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
|
||||||
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
|
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
|
||||||
]
|
]
|
||||||
distlib = []
|
distlib = [
|
||||||
|
{file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"},
|
||||||
|
{file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
|
||||||
|
]
|
||||||
|
execnet = [
|
||||||
|
{file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"},
|
||||||
|
{file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"},
|
||||||
|
]
|
||||||
filelock = [
|
filelock = [
|
||||||
{file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"},
|
{file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"},
|
||||||
{file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"},
|
{file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"},
|
||||||
]
|
]
|
||||||
glob2 = []
|
glob2 = [
|
||||||
|
{file = "glob2-0.7.tar.gz", hash = "sha256:85c3dbd07c8aa26d63d7aacee34fa86e9a91a3873bc30bf62ec46e531f92ab8c"},
|
||||||
|
]
|
||||||
importlib-metadata = [
|
importlib-metadata = [
|
||||||
{file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"},
|
{file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"},
|
||||||
{file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"},
|
{file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"},
|
||||||
|
@ -370,7 +419,10 @@ iniconfig = [
|
||||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||||
]
|
]
|
||||||
mako = []
|
mako = [
|
||||||
|
{file = "Mako-1.2.1-py3-none-any.whl", hash = "sha256:df3921c3081b013c8a2d5ff03c18375651684921ae83fd12e64800b7da923257"},
|
||||||
|
{file = "Mako-1.2.1.tar.gz", hash = "sha256:f054a5ff4743492f1aa9ecc47172cb33b42b9d993cffcc146c9de17e717b0307"},
|
||||||
|
]
|
||||||
markupsafe = [
|
markupsafe = [
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
|
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
|
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
|
||||||
|
@ -446,9 +498,17 @@ packaging = [
|
||||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
||||||
]
|
]
|
||||||
parse = []
|
parse = [
|
||||||
parse-type = []
|
{file = "parse-1.19.0.tar.gz", hash = "sha256:9ff82852bcb65d139813e2a5197627a94966245c897796760a3a2a8eb66f020b"},
|
||||||
platformdirs = []
|
]
|
||||||
|
parse-type = [
|
||||||
|
{file = "parse_type-0.6.0-py2.py3-none-any.whl", hash = "sha256:c148e88436bd54dab16484108e882be3367f44952c649c9cd6b82a7370b650cb"},
|
||||||
|
{file = "parse_type-0.6.0.tar.gz", hash = "sha256:20b43c660e48ed47f433bce5873a2a3d4b9b6a7ba47bd7f7d2a7cec4bec5551f"},
|
||||||
|
]
|
||||||
|
platformdirs = [
|
||||||
|
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
|
||||||
|
{file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
|
||||||
|
]
|
||||||
pluggy = [
|
pluggy = [
|
||||||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
||||||
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
||||||
|
@ -465,16 +525,30 @@ pytest = [
|
||||||
{file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
|
{file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
|
||||||
{file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
|
{file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
|
||||||
]
|
]
|
||||||
|
pytest-forked = [
|
||||||
|
{file = "pytest-forked-1.4.0.tar.gz", hash = "sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e"},
|
||||||
|
{file = "pytest_forked-1.4.0-py3-none-any.whl", hash = "sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"},
|
||||||
|
]
|
||||||
|
pytest-xdist = [
|
||||||
|
{file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"},
|
||||||
|
{file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"},
|
||||||
|
]
|
||||||
six = [
|
six = [
|
||||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||||
]
|
]
|
||||||
toml = []
|
toml = [
|
||||||
|
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||||
|
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||||
|
]
|
||||||
tomli = [
|
tomli = [
|
||||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||||
]
|
]
|
||||||
tox = []
|
tox = [
|
||||||
|
{file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"},
|
||||||
|
{file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"},
|
||||||
|
]
|
||||||
typed-ast = [
|
typed-ast = [
|
||||||
{file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"},
|
{file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"},
|
||||||
{file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"},
|
{file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"},
|
||||||
|
@ -501,12 +575,18 @@ typed-ast = [
|
||||||
{file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"},
|
{file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"},
|
||||||
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
|
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
|
||||||
]
|
]
|
||||||
types-setuptools = []
|
types-setuptools = [
|
||||||
|
{file = "types-setuptools-57.4.18.tar.gz", hash = "sha256:8ee03d823fe7fda0bd35faeae33d35cb5c25b497263e6a58b34c4cfd05f40bcf"},
|
||||||
|
{file = "types_setuptools-57.4.18-py3-none-any.whl", hash = "sha256:9660b8774b12cd61b448e2fd87a667c02e7ec13ce9f15171f1d49a4654c4df6a"},
|
||||||
|
]
|
||||||
typing-extensions = [
|
typing-extensions = [
|
||||||
{file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
|
{file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
|
||||||
{file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
|
{file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
|
||||||
]
|
]
|
||||||
virtualenv = []
|
virtualenv = [
|
||||||
|
{file = "virtualenv-20.15.1-py2.py3-none-any.whl", hash = "sha256:b30aefac647e86af6d82bfc944c556f8f1a9c90427b2fb4e3bfbf338cb82becf"},
|
||||||
|
{file = "virtualenv-20.15.1.tar.gz", hash = "sha256:288171134a2ff3bfb1a2f54f119e77cd1b81c29fc1265a2356f3e8d14c7d58c4"},
|
||||||
|
]
|
||||||
zipp = [
|
zipp = [
|
||||||
{file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"},
|
{file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"},
|
||||||
{file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"},
|
{file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"},
|
||||||
|
|
|
@ -40,11 +40,13 @@ Mako = "*"
|
||||||
parse = "*"
|
parse = "*"
|
||||||
parse-type = "*"
|
parse-type = "*"
|
||||||
pytest = ">=5.0"
|
pytest = ">=5.0"
|
||||||
|
typing-extensions = "*"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
tox = "^3.25.1"
|
tox = "^3.25.1"
|
||||||
mypy = "^0.961"
|
mypy = "^0.961"
|
||||||
types-setuptools = "^57.4.18"
|
types-setuptools = "^57.4.18"
|
||||||
|
pytest-xdist = "^2.5.0"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
|
|
@ -9,7 +9,7 @@ import py
|
||||||
from mako.lookup import TemplateLookup
|
from mako.lookup import TemplateLookup
|
||||||
|
|
||||||
from .feature import get_features
|
from .feature import get_features
|
||||||
from .scenario import find_argumented_step_fixture_name, make_python_docstring, make_python_name, make_string_literal
|
from .scenario import find_argumented_step_function, make_python_docstring, make_python_name, make_string_literal
|
||||||
from .steps import get_step_fixture_name
|
from .steps import get_step_fixture_name
|
||||||
from .types import STEP_TYPES
|
from .types import STEP_TYPES
|
||||||
|
|
||||||
|
@ -132,9 +132,9 @@ def _find_step_fixturedef(
|
||||||
if fixturedefs is not None:
|
if fixturedefs is not None:
|
||||||
return fixturedefs
|
return fixturedefs
|
||||||
|
|
||||||
argumented_step_name = find_argumented_step_fixture_name(name, type_, fixturemanager)
|
step_func_context = find_argumented_step_function(name, type_, fixturemanager)
|
||||||
if argumented_step_name is not None:
|
if step_func_context is not None:
|
||||||
return fixturemanager.getfixturedefs(argumented_step_name, item.nodeid)
|
return fixturemanager.getfixturedefs(step_func_context.name, item.nodeid)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,17 +12,16 @@ test_publish_article = scenario(
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import collections
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from typing import TYPE_CHECKING, Callable, cast
|
from typing import TYPE_CHECKING, Callable, cast
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.fixtures import FixtureLookupError, FixtureManager, FixtureRequest, call_fixture_func
|
from _pytest.fixtures import FixtureManager, FixtureRequest, call_fixture_func
|
||||||
|
|
||||||
from . import exceptions
|
from . import exceptions
|
||||||
from .feature import get_feature, get_features
|
from .feature import get_feature, get_features
|
||||||
from .steps import get_step_fixture_name, inject_fixture
|
from .steps import StepFunctionContext, inject_fixture
|
||||||
from .utils import CONFIG_STACK, get_args, get_caller_module_locals, get_caller_module_path
|
from .utils import CONFIG_STACK, get_args, get_caller_module_locals, get_caller_module_path
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -36,98 +35,67 @@ PYTHON_REPLACE_REGEX = re.compile(r"\W")
|
||||||
ALPHA_REGEX = re.compile(r"^\d+_*")
|
ALPHA_REGEX = re.compile(r"^\d+_*")
|
||||||
|
|
||||||
|
|
||||||
def find_argumented_step_fixture_name(name: str, type_: str, fixturemanager: FixtureManager) -> str | None:
|
def find_argumented_step_function(name: str, type_: str, fixturemanager: FixtureManager) -> StepFunctionContext | None:
|
||||||
"""Find argumented step fixture name."""
|
"""Find argumented step fixture name."""
|
||||||
# happens to be that _arg2fixturedefs is changed during the iteration so we use a copy
|
# happens to be that _arg2fixturedefs is changed during the iteration so we use a copy
|
||||||
for fixturename, fixturedefs in list(fixturemanager._arg2fixturedefs.items()):
|
for fixturename, fixturedefs in list(fixturemanager._arg2fixturedefs.items()):
|
||||||
for fixturedef in fixturedefs:
|
for fixturedef in reversed(fixturedefs):
|
||||||
parser = getattr(fixturedef.func, "_pytest_bdd_parser", None)
|
step_func_context = getattr(fixturedef.func, "_pytest_bdd_step_context", None)
|
||||||
if parser is None:
|
if step_func_context is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = parser.is_matching(name)
|
if step_func_context.type != type_:
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = step_func_context.parser.is_matching(name)
|
||||||
if not match:
|
if not match:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
parser_name = get_step_fixture_name(parser.name, type_)
|
return step_func_context
|
||||||
return parser_name
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _find_step_function(request: FixtureRequest, step: Step, scenario: Scenario) -> Callable[..., Any]:
|
|
||||||
"""Match the step defined by the regular expression pattern.
|
|
||||||
|
|
||||||
:param request: PyTest request object.
|
|
||||||
:param step: Step.
|
|
||||||
:param scenario: Scenario.
|
|
||||||
|
|
||||||
:return: Function of the step.
|
|
||||||
:rtype: function
|
|
||||||
"""
|
|
||||||
name = step.name
|
|
||||||
try:
|
|
||||||
# Simple case where no parser is used for the step
|
|
||||||
return request.getfixturevalue(get_step_fixture_name(name, step.type))
|
|
||||||
except FixtureLookupError as e:
|
|
||||||
try:
|
|
||||||
# Could not find a fixture with the same name, let's see if there is a parser involved
|
|
||||||
argumented_name = find_argumented_step_fixture_name(name, step.type, request._fixturemanager)
|
|
||||||
if argumented_name:
|
|
||||||
return request.getfixturevalue(argumented_name)
|
|
||||||
raise e
|
|
||||||
except FixtureLookupError as e2:
|
|
||||||
raise exceptions.StepDefinitionNotFoundError(
|
|
||||||
f"Step definition is not found: {step}. "
|
|
||||||
f'Line {step.line_number} in scenario "{scenario.name}" in the feature "{scenario.feature.filename}"'
|
|
||||||
) from e2
|
|
||||||
|
|
||||||
|
|
||||||
def _execute_step_function(
|
def _execute_step_function(
|
||||||
request: FixtureRequest, scenario: Scenario, step: Step, step_func: Callable[..., Any]
|
request: FixtureRequest, scenario: Scenario, step: Step, context: StepFunctionContext
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Execute step function.
|
"""Execute step function."""
|
||||||
|
kw = {
|
||||||
:param request: PyTest request.
|
"request": request,
|
||||||
:param scenario: Scenario.
|
"feature": scenario.feature,
|
||||||
:param step: Step.
|
"scenario": scenario,
|
||||||
:param function step_func: Step function.
|
"step": step,
|
||||||
:param example: Example table.
|
"step_func": context.step_func,
|
||||||
"""
|
"step_func_args": {},
|
||||||
kw = {"request": request, "feature": scenario.feature, "scenario": scenario, "step": step, "step_func": step_func}
|
}
|
||||||
|
|
||||||
request.config.hook.pytest_bdd_before_step(**kw)
|
request.config.hook.pytest_bdd_before_step(**kw)
|
||||||
kw["step_func_args"] = {}
|
|
||||||
|
# Get the step argument values.
|
||||||
|
converters = context.converters
|
||||||
|
kwargs = {}
|
||||||
|
args = get_args(context.step_func)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get the step argument values.
|
for arg, value in context.parser.parse_arguments(step.name).items():
|
||||||
converters = step_func._pytest_bdd_converters
|
if arg in converters:
|
||||||
kwargs = {}
|
value = converters[arg](value)
|
||||||
|
kwargs[arg] = value
|
||||||
|
|
||||||
for parser in step_func._pytest_bdd_parsers:
|
|
||||||
if not parser.is_matching(step.name):
|
|
||||||
continue
|
|
||||||
for arg, value in parser.parse_arguments(step.name).items():
|
|
||||||
if arg in converters:
|
|
||||||
value = converters[arg](value)
|
|
||||||
kwargs[arg] = value
|
|
||||||
break
|
|
||||||
|
|
||||||
args = get_args(step_func)
|
|
||||||
kwargs = {arg: kwargs[arg] if arg in kwargs else request.getfixturevalue(arg) for arg in args}
|
kwargs = {arg: kwargs[arg] if arg in kwargs else request.getfixturevalue(arg) for arg in args}
|
||||||
kw["step_func_args"] = kwargs
|
kw["step_func_args"] = kwargs
|
||||||
|
|
||||||
request.config.hook.pytest_bdd_before_step_call(**kw)
|
request.config.hook.pytest_bdd_before_step_call(**kw)
|
||||||
target_fixture = step_func._pytest_bdd_target_fixture
|
|
||||||
|
|
||||||
# Execute the step as if it was a pytest fixture, so that we can allow "yield" statements in it
|
# Execute the step as if it was a pytest fixture, so that we can allow "yield" statements in it
|
||||||
return_value = call_fixture_func(fixturefunc=step_func, request=request, kwargs=kwargs)
|
return_value = call_fixture_func(fixturefunc=context.step_func, request=request, kwargs=kwargs)
|
||||||
if target_fixture:
|
|
||||||
inject_fixture(request, target_fixture, return_value)
|
|
||||||
|
|
||||||
request.config.hook.pytest_bdd_after_step(**kw)
|
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
request.config.hook.pytest_bdd_step_error(exception=exception, **kw)
|
request.config.hook.pytest_bdd_step_error(exception=exception, **kw)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
if context.target_fixture is not None:
|
||||||
|
inject_fixture(request, context.target_fixture, return_value)
|
||||||
|
|
||||||
|
request.config.hook.pytest_bdd_after_step(**kw)
|
||||||
|
|
||||||
|
|
||||||
def _execute_scenario(feature: Feature, scenario: Scenario, request: FixtureRequest) -> None:
|
def _execute_scenario(feature: Feature, scenario: Scenario, request: FixtureRequest) -> None:
|
||||||
"""Execute the scenario.
|
"""Execute the scenario.
|
||||||
|
@ -139,22 +107,23 @@ def _execute_scenario(feature: Feature, scenario: Scenario, request: FixtureRequ
|
||||||
"""
|
"""
|
||||||
request.config.hook.pytest_bdd_before_scenario(request=request, feature=feature, scenario=scenario)
|
request.config.hook.pytest_bdd_before_scenario(request=request, feature=feature, scenario=scenario)
|
||||||
|
|
||||||
try:
|
for step in scenario.steps:
|
||||||
# Execute scenario steps
|
context = find_argumented_step_function(step.name, step.type, request._fixturemanager)
|
||||||
for step in scenario.steps:
|
if context is None:
|
||||||
try:
|
exc = exceptions.StepDefinitionNotFoundError(
|
||||||
step_func = _find_step_function(request, step, scenario)
|
f"Step definition is not found: {step}. "
|
||||||
except exceptions.StepDefinitionNotFoundError as exception:
|
f'Line {step.line_number} in scenario "{scenario.name}" in the feature "{scenario.feature.filename}"'
|
||||||
request.config.hook.pytest_bdd_step_func_lookup_error(
|
)
|
||||||
request=request, feature=feature, scenario=scenario, step=step, exception=exception
|
request.config.hook.pytest_bdd_step_func_lookup_error(
|
||||||
)
|
request=request, feature=feature, scenario=scenario, step=step, exception=exc
|
||||||
raise
|
)
|
||||||
_execute_step_function(request, scenario, step, step_func)
|
raise exc
|
||||||
finally:
|
step_func_context = context
|
||||||
request.config.hook.pytest_bdd_after_scenario(request=request, feature=feature, scenario=scenario)
|
|
||||||
|
|
||||||
|
try:
|
||||||
FakeRequest = collections.namedtuple("FakeRequest", ["module"])
|
_execute_step_function(request, scenario, step, step_func_context)
|
||||||
|
finally:
|
||||||
|
request.config.hook.pytest_bdd_after_scenario(request=request, feature=feature, scenario=scenario)
|
||||||
|
|
||||||
|
|
||||||
def _get_scenario_decorator(
|
def _get_scenario_decorator(
|
||||||
|
|
|
@ -36,18 +36,30 @@ def given_beautiful_article(article):
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
from typing import Any, Callable, TypeVar
|
from typing import Any, Callable, TypeVar
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.fixtures import FixtureDef, FixtureRequest
|
from _pytest.fixtures import FixtureDef, FixtureRequest
|
||||||
|
from typing_extensions import Literal
|
||||||
|
|
||||||
from .parsers import StepParser, get_parser
|
from .parsers import StepParser, get_parser
|
||||||
from .types import GIVEN, THEN, WHEN
|
from .types import GIVEN, THEN, WHEN
|
||||||
from .utils import get_caller_module_locals, setdefault
|
from .utils import get_caller_module_locals
|
||||||
|
|
||||||
TCallable = TypeVar("TCallable", bound=Callable[..., Any])
|
TCallable = TypeVar("TCallable", bound=Callable[..., Any])
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StepFunctionContext:
|
||||||
|
name: str
|
||||||
|
type: Literal["given", "when", "then"]
|
||||||
|
step_func: Callable[..., Any]
|
||||||
|
parser: StepParser
|
||||||
|
converters: dict[str, Callable[..., Any]] = field(default_factory=dict)
|
||||||
|
target_fixture: str | None = None
|
||||||
|
|
||||||
|
|
||||||
def get_step_fixture_name(name: str, type_: str) -> str:
|
def get_step_fixture_name(name: str, type_: str) -> str:
|
||||||
"""Get step fixture name.
|
"""Get step fixture name.
|
||||||
|
|
||||||
|
@ -107,7 +119,7 @@ def then(
|
||||||
|
|
||||||
|
|
||||||
def _step_decorator(
|
def _step_decorator(
|
||||||
step_type: str,
|
step_type: Literal["given", "when", "then"],
|
||||||
step_name: str | StepParser,
|
step_name: str | StepParser,
|
||||||
converters: dict[str, Callable] | None = None,
|
converters: dict[str, Callable] | None = None,
|
||||||
target_fixture: str | None = None,
|
target_fixture: str | None = None,
|
||||||
|
@ -125,22 +137,25 @@ def _step_decorator(
|
||||||
converters = {}
|
converters = {}
|
||||||
|
|
||||||
def decorator(func: TCallable) -> TCallable:
|
def decorator(func: TCallable) -> TCallable:
|
||||||
parser_instance = get_parser(step_name)
|
parser = get_parser(step_name)
|
||||||
parsed_step_name = parser_instance.name
|
parsed_step_name = parser.name
|
||||||
|
|
||||||
def lazy_step_func() -> TCallable:
|
|
||||||
return func
|
|
||||||
|
|
||||||
lazy_step_func._pytest_bdd_parser = parser_instance
|
|
||||||
|
|
||||||
setdefault(func, "_pytest_bdd_parsers", []).append(parser_instance)
|
|
||||||
func._pytest_bdd_converters = converters
|
|
||||||
func._pytest_bdd_target_fixture = target_fixture
|
|
||||||
|
|
||||||
fixture_step_name = get_step_fixture_name(parsed_step_name, step_type)
|
fixture_step_name = get_step_fixture_name(parsed_step_name, step_type)
|
||||||
|
|
||||||
|
def step_function_marker() -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
step_function_marker._pytest_bdd_step_context = StepFunctionContext(
|
||||||
|
name=fixture_step_name,
|
||||||
|
type=step_type,
|
||||||
|
step_func=func,
|
||||||
|
parser=parser,
|
||||||
|
converters=converters,
|
||||||
|
target_fixture=target_fixture,
|
||||||
|
)
|
||||||
|
|
||||||
caller_locals = get_caller_module_locals()
|
caller_locals = get_caller_module_locals()
|
||||||
caller_locals[fixture_step_name] = pytest.fixture(name=fixture_step_name)(lazy_step_func)
|
caller_locals[fixture_step_name] = pytest.fixture(name=fixture_step_name)(step_function_marker)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
@ -177,7 +192,8 @@ def inject_fixture(request: FixtureRequest, arg: str, value: Any) -> None:
|
||||||
request.addfinalizer(fin)
|
request.addfinalizer(fin)
|
||||||
|
|
||||||
# inject fixture definition
|
# inject fixture definition
|
||||||
request._fixturemanager._arg2fixturedefs.setdefault(arg, []).insert(0, fd)
|
request._fixturemanager._arg2fixturedefs.setdefault(arg, []).append(fd)
|
||||||
|
|
||||||
# inject fixture value in request cache
|
# inject fixture value in request cache
|
||||||
request._fixture_defs[arg] = fd
|
request._fixture_defs[arg] = fd
|
||||||
if add_fixturename:
|
if add_fixturename:
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
from pytest_bdd.utils import collect_dumped_objects
|
||||||
|
|
||||||
|
|
||||||
|
def test_reuse_same_step_different_converters(testdir):
|
||||||
|
testdir.makefile(
|
||||||
|
".feature",
|
||||||
|
arguments=textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
Feature: Reuse same step with different converters
|
||||||
|
Scenario: Step function should be able to be decorated multiple times with different converters
|
||||||
|
Given I have a foo with int value 42
|
||||||
|
And I have a foo with str value 42
|
||||||
|
And I have a foo with float value 42
|
||||||
|
When pass
|
||||||
|
Then pass
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
testdir.makepyfile(
|
||||||
|
textwrap.dedent(
|
||||||
|
r"""
|
||||||
|
import pytest
|
||||||
|
from pytest_bdd import parsers, given, when, then, scenarios
|
||||||
|
from pytest_bdd.utils import dump_obj
|
||||||
|
|
||||||
|
scenarios("arguments.feature")
|
||||||
|
|
||||||
|
@given(parsers.re(r"^I have a foo with int value (?P<value>.*?)$"), converters={"value": int})
|
||||||
|
@given(parsers.re(r"^I have a foo with str value (?P<value>.*?)$"), converters={"value": str})
|
||||||
|
@given(parsers.re(r"^I have a foo with float value (?P<value>.*?)$"), converters={"value": float})
|
||||||
|
def _(value):
|
||||||
|
dump_obj(value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
@then("pass")
|
||||||
|
@when("pass")
|
||||||
|
def _():
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
result = testdir.runpytest("-s")
|
||||||
|
result.assert_outcomes(passed=1)
|
||||||
|
|
||||||
|
[int_value, str_value, float_value] = collect_dumped_objects(result)
|
||||||
|
assert type(int_value) is int
|
||||||
|
assert int_value == 42
|
||||||
|
|
||||||
|
assert type(str_value) is str
|
||||||
|
assert str_value == "42"
|
||||||
|
|
||||||
|
assert type(float_value) is float
|
||||||
|
assert float_value == 42.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_string_steps_dont_take_precedence(testdir):
|
||||||
|
"""Test that normal steps don't take precedence over the other steps."""
|
||||||
|
testdir.makefile(
|
||||||
|
".feature",
|
||||||
|
arguments=textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
Feature: Step precedence
|
||||||
|
Scenario: String steps don't take precedence over other steps
|
||||||
|
Given I have a foo with value 42
|
||||||
|
When pass
|
||||||
|
Then pass
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
testdir.makeconftest(
|
||||||
|
textwrap.dedent(
|
||||||
|
"""
|
||||||
|
from pytest_bdd import given, when, then, parsers
|
||||||
|
from pytest_bdd.utils import dump_obj
|
||||||
|
|
||||||
|
|
||||||
|
@given(parsers.re(r"^I have a foo with value (?P<value>.*?)$"))
|
||||||
|
def _(value):
|
||||||
|
dump_obj("re")
|
||||||
|
|
||||||
|
|
||||||
|
@then("pass")
|
||||||
|
@when("pass")
|
||||||
|
def _():
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testdir.makepyfile(
|
||||||
|
textwrap.dedent(
|
||||||
|
r"""
|
||||||
|
import pytest
|
||||||
|
from pytest_bdd import parsers, given, when, then, scenarios
|
||||||
|
from pytest_bdd.utils import dump_obj
|
||||||
|
|
||||||
|
scenarios("arguments.feature")
|
||||||
|
|
||||||
|
@given("I have a foo with value 42")
|
||||||
|
def _():
|
||||||
|
dump_obj("str")
|
||||||
|
return 42
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
result = testdir.runpytest("-s")
|
||||||
|
result.assert_outcomes(passed=1)
|
||||||
|
|
||||||
|
[which] = collect_dumped_objects(result)
|
||||||
|
assert which == "re"
|
|
@ -4,6 +4,8 @@ Check the parent givens are collected and overridden in the local conftest.
|
||||||
"""
|
"""
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
|
from pytest_bdd.utils import collect_dumped_objects
|
||||||
|
|
||||||
|
|
||||||
def test_parent(testdir):
|
def test_parent(testdir):
|
||||||
"""Test parent given is collected.
|
"""Test parent given is collected.
|
||||||
|
@ -58,42 +60,49 @@ def test_parent(testdir):
|
||||||
result.assert_outcomes(passed=1)
|
result.assert_outcomes(passed=1)
|
||||||
|
|
||||||
|
|
||||||
def test_global_when_step(testdir, request):
|
def test_global_when_step(testdir):
|
||||||
"""Test when step defined in the parent conftest."""
|
"""Test when step defined in the parent conftest."""
|
||||||
|
|
||||||
|
testdir.makefile(
|
||||||
|
".feature",
|
||||||
|
global_when=textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
Feature: Global when
|
||||||
|
Scenario: Global when step defined in parent conftest
|
||||||
|
When I use a when step from the parent conftest
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
testdir.makeconftest(
|
testdir.makeconftest(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
"""\
|
"""\
|
||||||
from pytest_bdd import when
|
from pytest_bdd import when
|
||||||
|
from pytest_bdd.utils import dump_obj
|
||||||
|
|
||||||
@when("I use a when step from the parent conftest")
|
@when("I use a when step from the parent conftest")
|
||||||
def global_when():
|
def _():
|
||||||
pass
|
dump_obj("global when step")
|
||||||
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
subdir = testdir.mkpydir("subdir")
|
testdir.mkpydir("subdir").join("test_global_when.py").write(
|
||||||
|
|
||||||
subdir.join("test_library.py").write(
|
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
"""\
|
"""\
|
||||||
from pytest_bdd.steps import get_step_fixture_name, WHEN
|
from pytest_bdd import scenarios
|
||||||
|
|
||||||
def test_global_when_step(request):
|
scenarios("../global_when.feature")
|
||||||
assert request.getfixturevalue(
|
"""
|
||||||
get_step_fixture_name("I use a when step from the parent conftest",
|
|
||||||
WHEN,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
result = testdir.runpytest()
|
|
||||||
|
result = testdir.runpytest("-s")
|
||||||
result.assert_outcomes(passed=1)
|
result.assert_outcomes(passed=1)
|
||||||
|
|
||||||
|
[collected_object] = collect_dumped_objects(result)
|
||||||
|
assert collected_object == "global when step"
|
||||||
|
|
||||||
|
|
||||||
def test_child(testdir):
|
def test_child(testdir):
|
||||||
"""Test the child conftest overriding the fixture."""
|
"""Test the child conftest overriding the fixture."""
|
||||||
|
@ -198,7 +207,6 @@ def test_local(testdir):
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
"""\
|
"""\
|
||||||
from pytest_bdd import given, scenario
|
from pytest_bdd import given, scenario
|
||||||
from pytest_bdd.steps import get_step_fixture_name, GIVEN
|
|
||||||
|
|
||||||
|
|
||||||
@given("I have an overridable fixture", target_fixture="overridable")
|
@given("I have an overridable fixture", target_fixture="overridable")
|
||||||
|
@ -215,19 +223,6 @@ def test_local(testdir):
|
||||||
def test_local(request):
|
def test_local(request):
|
||||||
assert request.getfixturevalue("parent") == "local"
|
assert request.getfixturevalue("parent") == "local"
|
||||||
assert request.getfixturevalue("overridable") == "local"
|
assert request.getfixturevalue("overridable") == "local"
|
||||||
|
|
||||||
|
|
||||||
fixture = request.getfixturevalue(
|
|
||||||
get_step_fixture_name("I have a parent fixture", GIVEN)
|
|
||||||
)
|
|
||||||
assert fixture() == "local"
|
|
||||||
|
|
||||||
|
|
||||||
fixture = request.getfixturevalue(
|
|
||||||
get_step_fixture_name("I have an overridable fixture", GIVEN)
|
|
||||||
)
|
|
||||||
assert fixture() == "local"
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
from pytest_bdd.utils import collect_dumped_objects
|
||||||
|
|
||||||
|
|
||||||
|
def test_step_function_multiple_target_fixtures(testdir):
|
||||||
|
testdir.makefile(
|
||||||
|
".feature",
|
||||||
|
target_fixture=textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
Feature: Multiple target fixtures for step function
|
||||||
|
Scenario: A step can be decorated multiple times with different target fixtures
|
||||||
|
Given there is a foo with value "test foo"
|
||||||
|
And there is a bar with value "test bar"
|
||||||
|
Then foo should be "test foo"
|
||||||
|
And bar should be "test bar"
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
testdir.makepyfile(
|
||||||
|
textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
import pytest
|
||||||
|
from pytest_bdd import given, when, then, scenarios, parsers
|
||||||
|
from pytest_bdd.utils import dump_obj
|
||||||
|
|
||||||
|
scenarios("target_fixture.feature")
|
||||||
|
|
||||||
|
@given(parsers.parse('there is a foo with value "{value}"'), target_fixture="foo")
|
||||||
|
@given(parsers.parse('there is a bar with value "{value}"'), target_fixture="bar")
|
||||||
|
def _(value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
@then(parsers.parse('foo should be "{expected_value}"'))
|
||||||
|
def _(foo, expected_value):
|
||||||
|
dump_obj(foo)
|
||||||
|
assert foo == expected_value
|
||||||
|
|
||||||
|
@then(parsers.parse('bar should be "{expected_value}"'))
|
||||||
|
def _(bar, expected_value):
|
||||||
|
dump_obj(bar)
|
||||||
|
assert bar == expected_value
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
result = testdir.runpytest("-s")
|
||||||
|
result.assert_outcomes(passed=1)
|
||||||
|
|
||||||
|
[foo, bar] = collect_dumped_objects(result)
|
||||||
|
assert foo == "test foo"
|
||||||
|
assert bar == "test bar"
|
|
@ -1,40 +0,0 @@
|
||||||
"""Test when and then steps are callables."""
|
|
||||||
|
|
||||||
import textwrap
|
|
||||||
|
|
||||||
|
|
||||||
def test_when_then(testdir):
|
|
||||||
"""Test when and then steps are callable functions.
|
|
||||||
|
|
||||||
This test checks that when and then are not evaluated
|
|
||||||
during fixture collection that might break the scenario.
|
|
||||||
"""
|
|
||||||
testdir.makepyfile(
|
|
||||||
textwrap.dedent(
|
|
||||||
"""\
|
|
||||||
import pytest
|
|
||||||
from pytest_bdd import given, when, then
|
|
||||||
from pytest_bdd.steps import get_step_fixture_name, WHEN, THEN
|
|
||||||
|
|
||||||
@when("I do stuff")
|
|
||||||
def do_stuff():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@then("I check stuff")
|
|
||||||
def check_stuff():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def test_when_then(request):
|
|
||||||
do_stuff_ = request.getfixturevalue(get_step_fixture_name("I do stuff", WHEN))
|
|
||||||
assert callable(do_stuff_)
|
|
||||||
|
|
||||||
check_stuff_ = request.getfixturevalue(get_step_fixture_name("I check stuff", THEN))
|
|
||||||
assert callable(check_stuff_)
|
|
||||||
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
result = testdir.runpytest()
|
|
||||||
result.assert_outcomes(passed=1)
|
|
Loading…
Reference in New Issue