Cucumber-Expressions

Cucumber-expressions:

  • Similar idea like parse expressions

  • Provide a compact, readable placeholder syntax for step-parameters in step definitions

  • Support pre-defined parameter types with type conversion

  • Support to define own parameter types

  • Much easier to use compared to regular expressions

EXAMPLES: Step definitions with cucumber-expression
I have {int} cucumbers in my belly
I have {float} cucumbers in my belly
I have a {color} ball

Example

FILE: features/environment.py
from behave.cucumber_expression import use_step_matcher_for_cucumber_expressions

# -- DEFINE: cucumber-expressions as default step-matcher.
use_step_matcher_for_cucumber_expressions()
FILE: features/steps/example_steps_with_cucumber_expression.py
from behave import when
from behave.cucumber_expression import use_step_matcher_for_cucumber_expressions

# -- REQUIRED: If multiple step-matcher variants are used.
use_step_matcher_for_cucumber_expressions()

# -- MATCHES STEPS:
#   When I eat 1 apple
#   When I eat 10 apples
@when('I eat {int} apple(s)')
def step_when_eat_apples(ctx, number: int):
    assert isinstance(number, int)
    assert number >= 0
    ctx.apples_count = number

Predefined Parameter Types

ParameterType

Type

Description

{int}

int

Matches an 32-bit integer number (int) sand converts to it, like: 42

{float}

float

Matches float (as 32-bit float), like: 3.6, .8, -9.2

{word}

string

Matches one word without whitespace, like: banana (not: banana split).

{string}

string

Matches double-/single-quoted strings, for example "banana split" (not: banana split).

{}

string

Matches anything, like re_pattern = ".*"

{bigdecimal}

Decimal

Matches float, but converts to BigDecimal if platform supports it.

{double}

float

Matches float, but converts to 64-bit float number if platform supports it.

{biginteger}

int

Matches int, but converts to “BigInteger” if platform supports it.

{byte}

int

Matches int, but converts to 8-bit signed integer if platform supports it.

{short}

int

Matches int, but converts to 16-bit signed integer if platform supports it.

{long}

int

Matches int, but converts to 64-bit signed integer if platform supports it.

Use Optional Text

Sometimes, it is useful to match optional text (example: singular/plural).

EXAMPLE: Use optional word part
I have {int} cucumber(s) in my belly

# -- MATCHES:
I have 1 cucumber in my belly
I have 42 cucumbers in my belly

Use Alternative Words

Sometimes, it is useful to match two word alternatives.

EXAMPLE: Use word alternatives
I have {int} cucumber(s) in my belly/stomach

# -- MATCHES:
I have 1 cucumber in my belly
I have 42 cucumbers in my stomach

Escaping for Parenthesis/Braces

EXAMPLE: Use ESCAPING
I have {int} \{what} cucumber(s) in my belly \(amazing!)

# -- MATCHES:
I have 1 {what} cucumber in my belly (amazing!)
I have 42 {what} cucumbers in my belly (amazing!)

User-Defined Types

EXAMPLE 1: Number

FILE: features/steps/example1_number_steps.py
from behave import when
from behave.cucumber_expression import (
    define_parameter_type_with,
    use_step_matcher_for_cucumber_expressions
)
import parse

# -- TYPE-CONVERTER (aka: parse-function)
# HINT: Proof-of-concept (BETTER USE: {int})
@parse.with_pattern(r"\d+")
def parse_number(text):
    return int(text)

# -- DEFINE PARAMETER-TYPE:
# HINT: Each parameter type can be defined only once.
define_parameter_type_with(
    name="number",
    regexp=parse_number.pattern,
    type=int,
    transformer=parse_number
)

use_step_matcher_for_cucumber_expressions()

# -- MATCHES STEPS:
#   When I eat 1 apple
#   When I eat 10 apples
@when('I eat {number} apple(s)')
def step_when_eat_apples(ctx, the_number: int):
    assert isinstance(the_number, int)
    assert the_number >= 0
    ctx.apples_count = the_number

EXAMPLE 2: Parameter Type for Color enum

FILE: example4me/color.py
from enum import Enum

class Color(Enum):
    red = 1
    green = 2
    blue = 3
FILE: features/steps/example2_color_steps.py
from behave import when
from behave.cucumber_expression import (
    TypeBuilder,    # -- BASED ON: parse_type.TypeBuilder
    define_parameter_type_with,
    use_step_matcher_for_cucumber_expressions
)
from example4me.color import Color

# -- TYPE-CONVERTER(s):
parse_color = TypeBuilder.make_enum(Color)

# -- DEFINE PARAMETER-TYPE:
define_parameter_type_with(
    name="color",
    regexp=parse_color.pattern,
    type=Color,
    transformer=parse_color
)

use_step_matcher_for_cucumber_expressions()

# -- MATCHES STEPS:
# When I use the red color
# When I use the blue color
@when('I use the {color} color')
def step_when_use_color(ctx, the_color: Color):
    assert isinstance(the_color, Color)
    ctx.selected_color = the_color

EXAMPLE 3: Using more than one Parameter Type in a Step Definition

  • Ordering of parameters in step-function must match the ordering in the step definition text

FILE: features/steps/example3_colored_fruit_steps.py
from behave import when
from behave.cucumber_expression import (
    TypeBuilder,    # -- BASED ON: parse_type.TypeBuilder
    define_parameter_type_with,
    use_step_matcher_for_cucumber_expressions
)
from example4me.color import Color

# -- TYPE-CONVERTER(s):
SUPPORTED_FRUITS = ["apple", "banana", "pear"]
parse_color = TypeBuilder.make_enum(Color)
parse_fruit = TypeBuilder.make_choice(SUPPORTED_FRUITS)

# -- DEFINE PARAMETER-TYPE:
# HINT: Each parameter type can be defined only once.
define_parameter_type_with(
    name="color",
    regexp=parse_color.pattern,
    type=Color,
    transformer=parse_color
)
define_parameter_type_with(
    name="fruit",
    regexp=parse_fruit.pattern,
    type=str,
    transformer=parse_fruit
)

use_step_matcher_for_cucumber_expressions()

# -- MATCHES STEPS:
# When I eat the red apple
# When I eat the green banana
# When I eat the blue pear
@when('I eat the {color} {fruit}')
def step_when_eat_fruit(ctx, the_color: Color, the_fruit: str):
    assert isinstance(the_color, Color)
    assert isinstance(the_fruit, str)
    ctx.selected_fruit_combination = (the_color, the_fruit)

See also

Cucumber-Expressions: