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

Add ability to automatically collect *.feature files #342

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions pytest_bdd/bdd_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import sys

try:
from types import ModuleType
except ImportError:
ModuleType = type(sys)

from _pytest.python import Module
from .scenario import scenarios


class PytestBDDModule(Module):
"""
Class that represents *.feature file as python module
"""

def _getobj(self):
# Add _test suffix to module name
module_name = self.nodeid.replace("/", ".").rsplit(".", 1)[0] + "_test"

# Create dummy module to store tests
module = ModuleType(
name=module_name, doc="Autogenerated python module for {!r} feature file".format(str(self.fspath))
)
sys.modules[module_name] = module

# Add scenarios for dummy module
scenarios(self.fspath.basename, features_base_dir=self.fspath.dirname, module=module)

return module
10 changes: 10 additions & 0 deletions pytest_bdd/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from . import generation
from . import reporting
from . import gherkin_terminal_reporter
from .bdd_module import PytestBDDModule
from .utils import CONFIG_STACK


Expand Down Expand Up @@ -36,6 +37,7 @@ def pytest_addoption(parser):
def add_bdd_ini(parser):
parser.addini("bdd_features_base_dir", "Base features directory.")
parser.addini("bdd_strict_gherkin", "Parse features to be strict gherkin.", type="bool", default=True)
parser.addini("bdd_auto_collect", "To automatically collect feature files.", type="bool", default=False)


@pytest.mark.trylast
Expand Down Expand Up @@ -106,3 +108,11 @@ def item_key(item):
return (item.reportinfo()[:2], declaration_order)

items.sort(key=item_key)


def pytest_collect_file(path, parent):
"""
Automatically collect *.feature files and create test modules for them.
"""
if CONFIG_STACK[-1].getini("bdd_auto_collect") and path.ext == ".feature":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to support line number(s) too?

# run scenario defined on line 2
$ pytest features/foo.feature:2
# run scenarios defined on line 2 and 12
$ pytest features/foo.feature:2:12

return PytestBDDModule(path, parent)
7 changes: 5 additions & 2 deletions pytest_bdd/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,11 @@ def scenarios(*feature_paths, **kwargs):

:param *feature_paths: feature file paths to use for scenarios
"""
frame = inspect.stack()[1]
module = inspect.getmodule(frame[0])
if "module" in kwargs:
module = kwargs.pop("module")
else:
frame = inspect.stack()[1]
module = inspect.getmodule(frame[0])

features_base_dir = kwargs.get("features_base_dir")
if features_base_dir is None:
Expand Down
39 changes: 39 additions & 0 deletions tests/feature/test_auto_collect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Auto collect tests."""
import pytest

import textwrap


@pytest.fixture
def auto_collect_test_dir(testdir):
dirname = "test_auto_collect"
tests = testdir.mkpydir(dirname)

tests.join("foo.feature").write(
textwrap.dedent(
r"""
Feature: The feature
Scenario: Some scenario
"""
)
)


def test_auto_collect_tests(auto_collect_test_dir, testdir):
testdir.makeini(
"""
[pytest]
bdd_auto_collect=true
"""
)

result = testdir.runpytest()

result.stdout.fnmatch_lines("collected 1 item")
result.assert_outcomes(passed=1)


def test_auto_collect_tests_disabled(auto_collect_test_dir, testdir):
result = testdir.runpytest()

result.stdout.fnmatch_lines("collected 0 items")