Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for scenario descriptions to be present #312

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
37 changes: 34 additions & 3 deletions pytest_bdd/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
COMMENT_RE = re.compile(r"(^|(?<=\s))#")
SPLIT_LINE_RE = re.compile(r"(?<!\\)\|")

TYPES_WITH_DESCRIPTIONS = [types.FEATURE, types.SCENARIO, types.SCENARIO_OUTLINE]


def get_step_type(line):
"""Detect step type by the beginning of the line.
Expand Down Expand Up @@ -293,7 +295,8 @@ def __init__(self, basedir, filename, encoding="utf-8", strict_gherkin=True):
multiline_step = False
stripped_line = line.strip()
clean_line = strip_comments(line)
if not clean_line and (not prev_mode or prev_mode not in types.FEATURE):
if not clean_line and (not prev_mode or prev_mode not in TYPES_WITH_DESCRIPTIONS):
# Blank lines are included in feature and scenario descriptions
continue
mode = get_step_type(clean_line) or mode

Expand Down Expand Up @@ -347,7 +350,9 @@ def __init__(self, basedir, filename, encoding="utf-8", strict_gherkin=True):
self.line_number = line_number
self.tags = get_tags(prev_line)
elif prev_mode == types.FEATURE:
description.append(clean_line)
# Do not include comments in descriptions
if not stripped_line.startswith("#"):
description.append(line)
else:
raise exceptions.FeatureError(
"Multiple features are not allowed in a single feature file",
Expand All @@ -361,6 +366,16 @@ def __init__(self, basedir, filename, encoding="utf-8", strict_gherkin=True):
# Remove Feature, Given, When, Then, And
keyword, parsed_line = parse_line(clean_line)
if mode in [types.SCENARIO, types.SCENARIO_OUTLINE]:
# Lines between the scenario declaration
# and the scenario's first step line
# are considered part of the scenario description.
if scenario and not keyword:
# Do not include comments in descriptions
if stripped_line.startswith("#"):
continue
scenario.add_description_line(line)
continue

tags = get_tags(prev_line)
self.scenarios[parsed_line] = scenario = Scenario(self, parsed_line, line_number, tags=tags)
elif mode == types.BACKGROUND:
Expand Down Expand Up @@ -406,7 +421,7 @@ def __init__(self, basedir, filename, encoding="utf-8", strict_gherkin=True):
target.add_step(step)
prev_line = clean_line

self.description = u"\n".join(description).strip()
self.description = u"\n".join(description)

@classmethod
def get_feature(cls, base_path, filename, encoding="utf-8", strict_gherkin=True):
Expand Down Expand Up @@ -454,6 +469,7 @@ def __init__(self, feature, name, line_number, example_converters=None, tags=Non
self.tags = tags or set()
self.failed = False
self.test_function = None
self._description_lines = []

def add_step(self, step):
"""Add step to the scenario.
Expand All @@ -475,6 +491,21 @@ def steps(self):
result.extend(self._steps)
return result

def add_description_line(self, description_line):
"""Add a description line to the scenario.

:param str description_line:
"""
self._description_lines.append(description_line)

@property
def description(self):
"""Get the scenario's description.

:return: The scenario description
"""
return u"\n".join(self._description_lines)

@property
def params(self):
"""Get parameter names.
Expand Down
4 changes: 4 additions & 0 deletions tests/feature/description.feature
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ Feature: Description

Scenario: Description

Also, the scenario can have a description.

It goes here between the scenario name
and the first step.
Given I have a bar
60 changes: 52 additions & 8 deletions tests/feature/test_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,65 @@
from pytest_bdd import scenario


TEST_FILE_CONTENTS = """
import pytest
from pytest_bdd import given, scenario

def test_descriptions(request):
@scenario(
"descriptions.feature",
"Description",
)
def test():
pass

test(request)

@given("I have a bar")
def nothing():
pass

"""

with open("./tests/feature/description.feature") as f:
FEATURE_FILE_CONTENTS = f.read()

EXPECTED_FEATURE_DESCRIPTION = (
"\n"
" In order to achieve something\n"
" I want something\n"
" Because it will be cool\n"
"\n"
"\n"
" Some description goes here.\n"
)


EXPECTED_SCENARIO_DESCRIPTION = (
"\n"
" Also, the scenario can have a description.\n"
"\n"
" It goes here between the scenario name\n"
" and the first step."
)


def test_description(request):
"""Test description for the feature."""

@scenario("description.feature", "Description")
def test():
pass

assert (
test.__scenario__.feature.description
== """In order to achieve something
I want something
Because it will be cool
assert test.__scenario__.feature.description == EXPECTED_FEATURE_DESCRIPTION
assert test.__scenario__.description == EXPECTED_SCENARIO_DESCRIPTION

test(request)

Some description goes here."""
)

test(request)
def test_scenarios_are_created_when_they_have_scenario_descriptions(testdir):
testdir.makepyfile(test_descriptions=TEST_FILE_CONTENTS)
testdir.makefile(".feature", descriptions=FEATURE_FILE_CONTENTS)

result = testdir.runpytest()
result.assert_outcomes(passed=1)