Feature: Step definitions parameters parsing¶
Step parameters often enable the reuse of steps, which can reduce the amount of code required. This methodology allows for the same step to be used multiple times within a single scenario, but with different arguments. There are an multiple step parameter parsers available for your use.
Rule: Step definitions parameters parsing
Background:
Given File “Parametrized.feature” with content:
Feature: Step arguments Scenario: Every step takes a parameter with the same name Given I have a wallet Given I have 6 Euro When I lose 3 Euro And I pay 2 Euro Then I should have 1 Euro # In my dream... And I should have 999999 Euro
Example: Heuristic parser guesses a type and builds particular parser to be applied
Tries to select right parser between string, cucumber_expression,
cfparse and re. Any object that supports __str__ interface and does
not support parser interface will be wrapped with this parser
Given File “conftest.py” with content:
import pytest from pytest_bdd import given, when, then @pytest.fixture def values(): return [6, 3, 2, 1, 999999] # string parser @given("I have a wallet", param_defaults={'wallet': 'wallet'}) def i_have_wallet(wallet): assert wallet == 'wallet' # cucumber expressions parser @given("I have {int} Euro", anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_have(euro, values): assert euro == values.pop(0) # parse parser @when( "I pay {} Euro", anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_pay(euro, values): assert euro == values.pop(0) # cfparse parser @when("I lose {euro:d} Euro", converters=dict(euro=int)) def i_lose(euro, values): assert euro == values.pop(0) # regular expression parser @then( r"I should have (\d+) Euro", anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_should_have(euro, values): assert euro == values.pop(0)
When run pytest
Then pytest outcome must contain tests with statuses:
passed
1
Example: by “parse”
parse step parser
Provides a simple parser that replaces regular expressions for step
parameters with a readable syntax like {param:Type}. The syntax is
inspired by the Python builtin string.format() function. Step
parameters must use the named fields syntax of pypi_parse_ in step
definitions. The named fields are extracted, optionally type converted
and then used as step function arguments. Supports type conversions by
using type converters passed via extra_types
Given File “conftest.py” with content:
import pytest from pytest_bdd import given, when, then from parse import Parser as parse @pytest.fixture def values(): return [6, 3, 2, 1, 999999] @given(parse("I have a wallet"), param_defaults={'wallet': 'wallet'}) def i_have_wallet(wallet): assert wallet == 'wallet' @given(parse("I have {euro:g} Euro")) def i_have(euro, values): assert euro == values.pop(0) @when(parse("I pay {euro:d} Euro")) def i_pay(euro, values): assert euro == values.pop(0) @when( parse("I lose {} Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_pay(euro, values): assert euro == values.pop(0) @then( parse(r"I should have {:d} Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_should_have(euro, values): assert euro == values.pop(0)
When run pytest
Then pytest outcome must contain tests with statuses:
passed
1
Example: by “cfparse”
cfparse step parser
Provides an extended parser with “Cardinality Field” (CF) support.
Automatically creates missing type converters for related cardinality as
long as a type converter for cardinality=1 is provided. Supports parse
expressions like: {values:Type+} (cardinality=1..N, many)
{values:Type*} (cardinality=0..N, many0) {value:Type?}
(cardinality=0..1, optional) Supports type conversions (as above).
Given File “conftest.py” with content:
import pytest from pytest_bdd import given, when, then from parse_type.cfparse import Parser as parse @pytest.fixture def values(): return [6, 3, 2, 1, 999999] @given(parse("I have a wallet"), param_defaults={'wallet': 'wallet'}) def i_have_wallet(wallet): assert wallet == 'wallet' @given(parse("I have {euro:Number} Euro", extra_types=dict(Number=int))) def i_have(euro, values): assert euro == values.pop(0) @when(parse("I pay {euro:d} Euro")) def i_pay(euro, values): assert euro == values.pop(0) @when( parse("I lose {} Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_pay(euro, values): assert euro == values.pop(0) @then( parse(r"I should have {:d} Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_should_have(euro, values): assert euro == values.pop(0)
When run pytest
Then pytest outcome must contain tests with statuses:
passed
1
Example: by “cucumber-expressions”
cucumber expression step parser
Cucumber Expressions is an alternative to Regular Expressions with a more intuitive syntax.
And File “conftest.py” with content:
from functools import partial import pytest from pytest_bdd import given, when, then from cucumber_expressions.parameter_type_registry import ParameterTypeRegistry from cucumber_expressions.expression import CucumberExpression parse = partial( CucumberExpression, parameter_type_registry = ParameterTypeRegistry() ) @pytest.fixture def values(): return [6, 3, 2, 1, 999999] @given(parse("I have a wallet"), param_defaults={'wallet': 'wallet'}) def i_have_wallet(wallet): assert wallet == 'wallet' @given( parse("I have {int} Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_have(euro, values): assert euro == values.pop(0) @when( parse("I pay {} Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_pay(euro, values, request): assert euro == values.pop(0) @when( parse(r"I lose {int} Dollar/Euro(s)"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_lose(euro, values): assert euro == values.pop(0) @then( parse("I should have {int} Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_should_have(euro, values): assert euro == values.pop(0)
When run pytest
Then pytest outcome must contain tests with statuses:
passed
1
Example: by Cucumber regular expression step parser
And File “conftest.py” with content:
import pytest from pytest_bdd import given, when, then from functools import partial from cucumber_expressions.parameter_type_registry import ParameterTypeRegistry from cucumber_expressions.regular_expression import ( RegularExpression as CucumberRegularExpression ) parse = partial( CucumberRegularExpression, parameter_type_registry = ParameterTypeRegistry() ) @pytest.fixture def values(): return [6, 3, 2, 1, 999999] @given(parse("I have a wallet"), param_defaults={'wallet': 'wallet'}) def i_have_wallet(wallet): assert wallet == 'wallet' @given( parse(r"I have (\d+) Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_have(euro, values): assert euro == values.pop(0) @when( parse("I pay (.*) Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_pay(euro, values, request): assert euro == values.pop(0) @when( parse(r"I lose (.+) Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_lose(euro, values): assert euro == values.pop(0) @then( parse(r"I should have (\d+) Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_should_have(euro, values): assert euro == values.pop(0)
When run pytest
Then pytest outcome must contain tests with statuses:
passed
1
Example: by “regular-expressions”
This uses full regular expressions to parse the clause text. You will need to use named groups “(?P…)” to define the variables pulled from the text and passed to your “step()” function. Type conversion can only be done via “converters” step decorator argument (see example in according feature).
Given File “conftest.py” with content:
import pytest from pytest_bdd import given, when, then from re import compile as parse @pytest.fixture def values(): return [6, 3, 2, 1, 999999] @given(parse("I have a wallet"), param_defaults={'wallet': 'wallet'}) def i_have_wallet(wallet): assert wallet == 'wallet' @given(parse(r"I have (?P<euro>\d+) Euro"), converters=dict(euro=int)) def i_have(euro, values): assert euro == values.pop(0) @when( parse(r"I pay (\d+) Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_pay(euro, values): assert euro == values.pop(0) @when(parse(r"I lose (.+) Euro"), anonymous_group_names=('euro',), converters=dict(euro=int) ) def i_lose(euro, values): assert euro == values.pop(0) @then(parse(r"I should have (?P<euro>\d+) Euro"), converters=dict(euro=int)) def i_should_have(euro, values): assert euro == values.pop(0)
When run pytest
Then pytest outcome must contain tests with statuses:
passed
1