Add pytest_bdd_apply_tag hook

This commit is contained in:
Florian Bruhin 2016-06-21 18:27:02 +02:00
parent 6bed671dcf
commit c53f7a7519
6 changed files with 77 additions and 1 deletions

View File

@ -6,6 +6,7 @@ Changelog
- Fix FixtureDef signature for newer pytest versions (The-Compiler)
- Better error explanation for the steps defined outside of scenarios (olegpidsadnyi)
- Add a ``pytest_bdd_apply_tag`` hook to customize handling of tags (The-Compiler)
2.16.1

View File

@ -747,6 +747,19 @@ Note that if you use pytest `--strict` option, all bdd tags mentioned in the fea
`markers` setting of the `pytest.ini` config. Also for tags please use names which are python-compartible variable
names, eg starts with a non-number, underscore alphanumberic, etc. That way you can safely use tags for tests filtering.
You can customize how hooks are converted to pytest marks by implementing the
``pytest_bdd_apply_tag`` hook and returning ``True`` from it:
.. code-block:: python
def pytest_bdd_apply_tag(tag, function):
if tag == 'todo':
marker = pytest.mark.skip(reason="Not implemented yet")
marker(function)
return True
else:
# Fall back to pytest-bdd's default behavior
return None
Test setup
----------

View File

@ -1,3 +1,5 @@
import pytest
"""Pytest-bdd pytest hooks."""
@ -31,3 +33,13 @@ def pytest_bdd_step_validation_error(request, feature, scenario, step, step_func
def pytest_bdd_step_func_lookup_error(request, feature, scenario, step, exception):
"""Called when step lookup failed."""
@pytest.hookspec(firstresult=True)
def pytest_bdd_apply_tag(tag, function):
"""Apply a tag (from a ``.feature`` file) to the given scenario.
The default implementation does the equivalent of
``getattr(pytest.mark, tag)(function)``, but you can override this hook and
return ``True`` to do more sophisticated handling of tags.
"""

View File

@ -74,3 +74,9 @@ def pytest_bdd_after_step(request, feature, scenario, step, step_func, step_func
def pytest_cmdline_main(config):
generation.cmdline_main(config)
def pytest_bdd_apply_tag(tag, function):
mark = getattr(pytest.mark, tag)
mark(function)
return True

View File

@ -269,7 +269,7 @@ def _get_scenario_decorator(feature, feature_name, scenario, scenario_name, call
_scenario = pytest.mark.parametrize(*param_set)(_scenario)
for tag in scenario.tags.union(feature.tags):
_scenario = getattr(pytest.mark, tag)(_scenario)
pytest.config.hook.pytest_bdd_apply_tag(tag=tag, function=_scenario)
_scenario.__doc__ = "{feature_name}: {scenario_name}".format(
feature_name=feature_name, scenario_name=scenario_name)

View File

@ -95,3 +95,47 @@ def test_tags_after_background_issue_160(testdir):
result = testdir.runpytest('-m', 'tag', '-vv').parseoutcomes()
assert result['passed'] == 1
assert result['deselected'] == 1
def test_apply_tag_hook(testdir):
testdir.makeconftest("""
import pytest
@pytest.hookimpl(tryfirst=True)
def pytest_bdd_apply_tag(tag, function):
if tag == 'todo':
marker = pytest.mark.skipif(True, reason="Not implemented yet")
marker(function)
return True
else:
# Fall back to pytest-bdd's default behavior
return None
""")
testdir.makefile('.feature', test="""
Feature: Customizing tag handling
@todo
Scenario: Tags
Given I have a bar
@skip
Scenario: Tags 2
Given I have a bar
""")
testdir.makepyfile("""
from pytest_bdd import given, scenarios
@given('I have a bar')
def i_have_bar():
return 'bar'
scenarios('test.feature')
""")
result = testdir.runpytest('-rs')
result.stdout.fnmatch_lines(
[
"SKIP *: Not implemented yet",
"SKIP *: unconditional skip",
"*= 2 skipped * =*"
]
)