4

I'd like to define a single PyTest-BDD based Scenario Outline that has multiple Examples. Sample snippet:

        Scenario Outline: front to back validation
          When tester executes access view sql query <sqlCommandProp> into av dataframe
          And tester adds investment quant id to av dataframe
          And tester reads raw file <fileNameProp> from datalake into raw dataframe

        @raw2AccessValidation
        Examples:
       |sqlCommandProp|fileNameProp|
       |sqlCommand    | fileName   |

        @raw2AccessValidation2
       Examples:
       |sqlCommandProp|fileNameProp|
       |eric          | shane      |

I want to have separate tags for each Example, as I might not want to run them all.

I have tried the above, and see that multiple Examples are okay. However, I can't seem to have the different tags recognized, so I can't specify which of the 2 (or more) I want to run.

I ask because this is do-able with java/cucumber engine. Wondering if I am missing something, doing something wrong, with pytest-bdd ??

thnx

Automation Engr
  • 444
  • 2
  • 4
  • 26
Walter Kelt
  • 2,199
  • 1
  • 18
  • 22

1 Answers1

1

Based on this issue, it doesn't support it yet.

Solution1: Utilize the markers and register them within the pytest hooks

Until the mentioned issue is resolved, you can choose to utilize the pytest hooks and manually register the markers. The following code iterates through all the registered test items and registers new markers on them based on their respective feature files. It relies on several internal attributes of the pytest-bdd library, but it effectively functions even before the library incorporates the feature internally:

# conftest.py

import re
from pathlib import Path
from typing import List

import pytest


def pytest_collection_modifyitems(config: pytest.Config, items: List[pytest.Item]):
    for item in items:
        if not hasattr(item, "_pyfuncitem") or not hasattr(item, "callspec"):
            continue

        feature = item._pyfuncitem._obj.__scenario__.feature
        feature_content_lines = Path(feature.filename).read_text().splitlines()

        parameters = list(item.callspec.params["_pytest_bdd_example"].values())

        examples_start_line = None
        for i, line in enumerate(feature_content_lines):
            if "Examples:" in line:
                examples_start_line = i
                continue
            elif re.match(r"[|\s]+{}[|\s]+".format(r"[|\s]+".join(parameters)), line):
                break

        if examples_start_line is None:
            continue

        tag_match = re.search(
            r"@(?P<tag>\S+)", feature_content_lines[examples_start_line - 1]
        )

        if tag_match is None:
            continue

        tag = tag_match.group("tag")

        item.add_marker(tag)

Consider the following feature file:

Feature: Scenario outlines
    Scenario Outline: Outlined given, when, then
        Given there are <start> cucumbers
        When I eat <eat> cucumbers
        Then I should have <left> cucumbers

        @part1
        Examples:
        | start | eat | left |
        |  12   |  5  |  7   |
        |  12   |  4  |  8   |

        @part2
        Examples:
        | start | eat | left |
        |  11   |  5  |  6   |

And the following test file:

# test_scenario_outlines.py

from pytest_bdd import given, parsers, scenarios, then, when

scenarios("scenario_outlines.feature")


@given(parsers.parse("there are {start:d} cucumbers"), target_fixture="cucumbers")
def given_cucumbers(start):
    return {"start": start, "eat": 0}


@when(parsers.parse("I eat {eat:d} cucumbers"))
def eat_cucumbers(cucumbers, eat):
    cucumbers["eat"] += eat


@then(parsers.parse("I should have {left:d} cucumbers"))
def should_have_left_cucumbers(cucumbers, left):
    assert cucumbers["start"] - cucumbers["eat"] == left

You can simply use the markers to run specific example records:

pytest . -m part1

enter image description here

pytest . -m part2

enter image description here

Note: You should register your markers to prevent pytest warnings.

Solution 2: Utilize the parameters as tags

You can add a new column to your parameters table, specifying the tag:

# scenario_outlines.feature

Feature: Scenario outlines
    Scenario Outline: Outlined given, when, then
        Given there are <start> cucumbers
        When I eat <eat> cucumbers
        Then I should have <left> cucumbers

        Examples:
        | start | eat | left |   tag   |
        |  12   |  5  |  7   |  part1  |
        |  12   |  4  |  8   |  part1  |

        Examples:
        | start | eat | left |   tag   |
        |  11   |  5  |  6   |  part2  |

There's no need to edit the Python test files or pay attention to the tag parameter in the code. Instead, simply utilize the -k option to selectively choose records with a specific tag value:

pytest . -k part1

enter image description here

pytest . -k part2

enter image description here

Alireza Roshanzamir
  • 1,165
  • 6
  • 17
  • 1
    Yes, of course. In fact, I haven't made any changes to the `test_scenario_outlines.py` test file. Instead, I only modified the `scenario_outlines.feature` file and utilized the `-k` command line option. This approach successfully applies to the `test_scenario_outlines.py` file in the Solution 1. – Alireza Roshanzamir Jul 10 '23 at 16:21