Noteworthy in Version 1.2.7

Summary:

Support Gherkin v6 Grammar

Grammar changes:

  • Rule concept added to better correspond to Example Mapping concepts

  • Add aliases for Scenario and Scenario Outline (for similar reasons)

A Rule (or: business rule) allows to group multiple Scenario(s)/Example(s):

# -- RULE GRAMMAR PSEUDO-CODE:
@tag1 @tag2
Rule: Optional Rule Title...
    Description?        #< CARDINALITY: 0..1 (optional)
    Background?         #< CARDINALITY: 0..1 (optional)
    Scenario*           #< CARDINALITY: 0..N (many)
    ScenarioOutline*    #< CARDINALITY: 0..N (many)

Gherkin v6 keyword aliases:

Concept

Preferred Keyword

Alias(es)

Scenario

Example

Scenario

Scenario Outline

Scenario Outline

Scenario Template

Examples

Examples

Scenarios

Example:

# -- FILE: features/example_with_rules.feature
# USING: Gherkin v6
Feature: With Rules

  Background: Feature.Background
    Given feature background step_1

  Rule: Rule_1
    Background: Rule_1.Background
      Given rule_1 background step_1

    Example: Rule_1.Example_1
      Given rule_1 scenario_1 step_1

  Rule: Rule_2

    Example: Rule_2.Example_1
      Given rule_2 scenario_1 step_1

  Rule: Rule_3
    Background: Rule_3.EmptyBackground
    Example: Rule_3.Example_1
      Given rule_3 scenario_1 step_1

Overview of the Example Mapping concepts:

Cucumber: `Example Mapping`_

Hint

Gherkin v6 Grammar Issues

  • cucumber issue #632: Rule tags are currently only supported in behave. The Cucumber Gherkin v6 grammar currently lacks this functionality.

  • cucumber issue #590: Rule Background: A proposal is pending to remove Rule Backgrounds again

Tag-Expressions v2

Tag-Expressions v2 are based on cucumber-tag-expressions with some extensions:

  • Tag-Expressions v2 provide boolean logic expression (with and, or and not operators and parenthesis for grouping expressions)

  • Tag-Expressions v2 are far more readable and composable than Tag-Expressions v1

  • Some boolean-logic-expressions where not possible with Tag-Expressions v1

  • Therefore, Tag-Expressions v2 supersedes the old-style tag-expressions.

TAG-EXPRESSION EXAMPLES
# -- EXAMPLE 1: Select features/scenarios that have the tags: @a and @b
@a and @b

# -- EXAMPLE 2: Select features/scenarios that have the tag: @a or @b
@a or @b

# -- EXAMPLE 3: Select features/scenarios that do not have the tag: @a
not @a

# -- EXAMPLE 4: Select features/scenarios that have the tags: @a but not @b
@a and not @b

# -- EXAMPLE 5: Select features/scenarios that have the tags: (@a or @b) but not @c
# HINT: Boolean expressions can be grouped with parenthesis.
(@a or @b) and not @c

COMMAND-LINE EXAMPLE:

USING: Tag-Expressions v2 with behave
# -- SELECT-BY-TAG-EXPRESSION (with tag-expressions v2):
# Select all features / scenarios with both "@foo" and "@bar" tags.
$ behave --tags="@foo and @bar" features/

# -- EXAMPLE: Use default_tags from config-file "behave.ini".
# Use placeholder "{config.tags}" to refer to this tag-expression.
# HERE: config.tags = "not (@xfail or @not_implemented)"
$ behave --tags="(@foo or @bar) and {config.tags}" --tags-help
...
CURRENT TAG_EXPRESSION: ((foo or bar) and not (xfail or not_implemented))

# -- EXAMPLE: Uses Tag-Expression diagnostics with --tags-help option
$ behave --tags="(@foo and @bar) or @baz" --tags-help
$ behave --tags="(@foo and @bar) or @baz" --tags-help --verbose

Tag Matching with Tag-Expressions

Tag-Expressions v2 support partial string/tag matching with wildcards. This supports tag-expressions:

Tag Matching Idiom

Example 1

Example 2

Description

tag.starts_with

@foo.*

foo.*

Search for tags that start with a prefix.

tag.ends_with

@*.one

*.one

Search for tags that end with a suffix.

tag.contains

@*foo*

*foo*

Search for tags that contain a part.

FILE: features/one.feature
Feature: Alice

  @foo.one
  Scenario: Alice.1
    ...

  @foo.two
  Scenario: Alice.2
    ...

  @bar
  Scenario: Alice.3
    ...

The following command-line will select all features / scenarios with tags that start with “@foo.”:

USAGE EXAMPLE: Run behave with tag-matching expressions
$ behave -f plain --tags="@foo.*" features/one.feature
Feature: Alice

  Scenario: Alice.1
    ...

  Scenario: Alice.2
    ...

# -- HINT: Only Alice.1 and Alice.2 are matched (not: Alice.3).

Note

  • Filename matching wildcards are supported. See fnmatch (Unix style filename matching).

  • The tag matching functionality is an extension to cucumber-tag-expressions.

Select the Tag-Expression Version to Use

The tag-expression version, that should be used by behave, can be specified in the behave config-file.

This allows a user to select:

  • Tag-Expressions v1 (if needed)

  • Tag-Expressions v2 when it is feasible

EXAMPLE:

FILE: behave.ini
# SPECIFY WHICH TAG-EXPRESSION-PROTOCOL SHOULD BE USED:
#   SUPPORTED VALUES: v1, v2, auto_detect
#   CURRENT DEFAULT:  auto_detect
[behave]
tag_expression_protocol = v1    # -- Use Tag-Expressions v1.

Tag-Expressions v1

Tag-Expressions v1 are becoming deprecated (but are currently still supported). Use Tag-Expressions v2 instead.

Note

Tag-Expressions v1 support will be dropped in behave v1.4.0.

Select-by-location for Scenario Containers

In the past, it was already possible to scenario(s) by using its file-location.

A file-location has the schema: <FILENAME>:<LINE_NUMBER>. Example: features/alice.feature:12 (refers to line 12 in features/alice.feature file).

Rules to select Scenarios by using the file-location:

  • Scenario: Use a file-location that points to the keyword/title or its steps (until next Scenario/Entity starts).

  • Scenario of a ScenarioOutline: Use the file-location of its Examples row.

Now you can select all entities of a Scenario Container (Feature, Rule, ScenarioOutline):

  • Feature: Use file-location before first contained entity/Scenario starts.

  • Rule: Use file-location from keyword/title line to line before its first Scenario/Background.

  • ScenarioOutline: Use file-location from keyword/title line to line before its Examples rows.

A file-location into a Scenario Container selects all its entities (Scenarios, …).

Support for Emojis in Feature Files and Steps

  • Emojis can now be used in *.feature files.

  • Emojis can now be used in step definitions.

  • You can now use language=emoji (em) in *.feature files ;-)

# -- FILE: features/i18n_emoji.feature
# language: em
# SOURCE: https://github.com/cucumber/cucumber/blob/master/gherkin/testdata/good/i18n_emoji.feature
# HINT:
#   Temporarily disabled on os=win32 (Windows) until unicode encoding issues are fixed.
#   Try with environment variable: PYTHONUTF8=1

@not.with_os=win32
📚: 🙈🙉🙊

  📕: 💃
    😐🎸
# -- FILE: features/steps/i18n_emoji_steps.py
# -*- coding: UTF-8 -*-
# NEEDED-BY: features/i18n_emoji.feature

from behave import given

@given(u'🎸')
def step_impl(context):
    """Step implementation example with emoji(s)."""
    pass

Improve Active-Tags Logic

The active-tag computation logic was slightly changed (and fixed):

  • if multiple active-tags with same category are used

  • combination of positive active-tags (use.with_{category}={value}) and negative active-tags (not.with_{category}={value}) with same category are now supported

All active-tags with same category are combined into one category tag-group. The following logical expression is used for active-tags with the same category:

category_tag_group.enabled := positive-tag-expression and not negative-tag-expression
  positive-tag-expression  := enabled(tag1) or enabled(tag2) or ...
  negative-tag-expression  := enabled(tag3) or enabled(tag4) or ...
   tag1, tag2 are positive-tags, like @use.with_category=value
   tag3, tag4 are negative-tags, like @not.with_category=value

EXAMPLE:

Feature: Active-Tag Example

  @use.with_browser=Safari
  @use.with_browser=Chrome
  @not.with_browser=Firefox
  Scenario: Use one active-tag group/category

    HINT: Only executed with web browser Safari and Chrome, Firefox is explicitly excluded.
    ...

  @use.with_browser=Firefox
  @use.with_os=linux
  @use.with_os=darwin
  Scenario: Use two active-tag groups/categories

    HINT 1: Only executed with browser: Firefox
    HINT 2: Only executed on OS: Linux and Darwin (macOS)
    ...

Active-Tags: Use ValueObject for better Comparisons

The current mechanism of active-tags only supports the equals / equal-to comparison mechanism to determine if the tag.value matches the current.value, like:

# -- SCHEMA: "@use.with_{category}={value}" or "@not.with_{category}={value}"
@use.with_browser=Safari    # HINT: tag.value = "Safari"

ACTIVE TAG MATCHES, if: current.value == tag.value  (for string values)

The equals comparison method is sufficient for many situations. But in some situations, you want to use other comparison methods. The behave.tag_matcher.ValueObject class was added to allow the user to provide an own comparison method (and type conversion support).

EXAMPLE 1:

Feature: Active-Tag Example 1 with ValueObject

  @use.with_temperature.min_value=15
  Scenario: Only run if temperature >= 15 degrees Celcius
    ...
# -- FILE: features/environment.py
import operator
from behave.tag_matcher import ActiveTagMatcher, ValueObject
from my_system.sensors import Sensors

# -- SIMPLIFIED: Better use behave.tag_matcher.NumberValueObject
# CTOR: ValueObject(value, compare=operator.eq)
# HINT: Parameter "value" can be a getter-function (w/o args).
class NumberValueObject(ValueObject):
    def matches(self, tag_value):
        tag_number = int(tag_value)
        return self.compare(self.value, tag_number)

current_temperature = Sensors().get_temperature()
active_tag_value_provider = {
    # -- COMPARISON:
    # temperature.value:     current.value == tag.value  -- DEFAULT: equals  (eq)
    # temperature.min_value: current.value >= tag.value  -- greater_or_equal (ge)
    "temperature.value":     NumberValueObject(current_temperature),
    "temperature.min_value": NumberValueObject(current_temperature, operator.ge),
}
active_tag_matcher = ActiveTagMatcher(active_tag_value_provider)

# -- HOOKS SETUP FOR ACTIVE-TAGS: ... (omitted here)

EXAMPLE 2:

A slightly more complex situation arises, if you need to constrain the execution of an scenario to a temperature range, like:

Feature: Active-Tag Example 2 with Min/Max Value Range

  @use.with_temperature.min_value=10
  @use.with_temperature.max_value=70
  Scenario: Only run if temperature is between 10 and 70 degrees Celcius
    ...
# -- FILE: features/environment.py
...
current_temperature = Sensors().get_temperature()
active_tag_value_provider = {
    # -- COMPARISON:
    # temperature.min_value:  current.value >= tag.value
    # temperature.max_value:  current.value <= tag.value
    "temperature.min_value": NumberValueObject(current_temperature, operator.ge),
    "temperature.max_value": NumberValueObject(current_temperature, operator.le),
}
...

EXAMPLE 3:

Feature: Active-Tag Example 3 with Contains/Contained-in Comparison

  @use.with_supported_payment_method=VISA
  Scenario: Only run if VISA is one of the supported payment methods
    ...

  # OR: @use.with_supported_payment_methods.contains_value=VISA
# -- FILE: features/environment.py
# NORMALLY:
#  from my_system.payment import get_supported_payment_methods
#  payment_methods = get_supported_payment_methods()
...
payment_methods = ["VISA", "MasterCard", "paycheck"]
active_tag_value_provider = {
    # -- COMPARISON:
    # supported_payment_method: current.value contains tag.value
    "supported_payment_method": ValueObject(payment_methods, operator.contains),
}
...

Detect Bad Step Definitions

The regular expression (re) module in Python has increased the checks when bad regular expression patterns are used. Since Python >= 3.11, an re.error exception may be raised on some regular expressions. The exception is raised when the bad regular expression is compiled (on re.compile()).

behave has added the following support:

  • Detects a bad step-definition when they are added to the step-registry.

  • Reports a bad step-definition and their exception during this step.

  • bad step-definitions are not registered in the step-registry.

  • A bad step-definition is like an UNDEFINED step-definition.

  • A BadStepsFormatter formatter was added that shows any BAD STEP DEFINITIONS

Note

More Information on BAD STEP-DEFINITIONS: