From adbea28cb98f84f1feb4b86bf556c46199affbfc Mon Sep 17 00:00:00 2001 From: Nikolay Chillev Date: Thu, 26 Mar 2026 15:47:39 +0200 Subject: [PATCH] Print tags in gherkin-terminal-reporter output Display feature, scenario, and rule tags in the terminal reporter when using --gherkin-terminal-reporter with -v or -vv. Tags are shown after the feature/scenario/rule name with @ prefix, matching the Gherkin syntax convention. Closes #476 --- src/pytest_bdd/gherkin_terminal_reporter.py | 13 +++++ .../feature/test_gherkin_terminal_reporter.py | 53 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/pytest_bdd/gherkin_terminal_reporter.py b/src/pytest_bdd/gherkin_terminal_reporter.py index e18719c17..a77d41053 100644 --- a/src/pytest_bdd/gherkin_terminal_reporter.py +++ b/src/pytest_bdd/gherkin_terminal_reporter.py @@ -42,6 +42,13 @@ def configure(config: Config) -> None: raise Exception("gherkin-terminal-reporter is not compatible with 'xdist' plugin.") +def _format_tags(tags: list[str]) -> str: + """Format a list of tags for display, prefixed with @.""" + if not tags: + return "" + return " @" + " @".join(tags) + + class GherkinTerminalReporter(TerminalReporter): # type: ignore[misc] def __init__(self, config: Config) -> None: super().__init__(config) @@ -83,16 +90,19 @@ def pytest_runtest_logreport(self, report: TestReport) -> None: self.ensure_newline() self._tw.write(f"{scenario['feature']['keyword']}: ", **feature_markup) self._tw.write(scenario["feature"]["name"], **feature_markup) + self._tw.write(_format_tags(scenario["feature"].get("tags", [])), **feature_markup) self._tw.write("\n") if rule and rule["name"] != self.current_rule: self._tw.write(f" {rule['keyword']}: ", **rule_markup) self._tw.write(rule["name"], **rule_markup) + self._tw.write(_format_tags(rule.get("tags", [])), **rule_markup) self._tw.write("\n") self.current_rule = rule["name"] self._tw.write(f"{indent} {scenario['keyword']}: ", **scenario_markup) self._tw.write(scenario["name"], **scenario_markup) + self._tw.write(_format_tags(scenario.get("tags", [])), **scenario_markup) self._tw.write(" ") self._tw.write(word, **word_markup) self._tw.write("\n") @@ -100,16 +110,19 @@ def pytest_runtest_logreport(self, report: TestReport) -> None: self.ensure_newline() self._tw.write(f"{scenario['feature']['keyword']}: ", **feature_markup) self._tw.write(scenario["feature"]["name"], **feature_markup) + self._tw.write(_format_tags(scenario["feature"].get("tags", [])), **feature_markup) self._tw.write("\n") if rule and rule["name"] != self.current_rule: self._tw.write(f" {rule['keyword']}: ", **rule_markup) self._tw.write(rule["name"], **rule_markup) + self._tw.write(_format_tags(rule.get("tags", [])), **rule_markup) self._tw.write("\n") self.current_rule = rule["name"] self._tw.write(f"{indent} {scenario['keyword']}: ", **scenario_markup) self._tw.write(scenario["name"], **scenario_markup) + self._tw.write(_format_tags(scenario.get("tags", [])), **scenario_markup) self._tw.write("\n") for step in scenario["steps"]: self._tw.write(f"{indent} {step['keyword']} {step['name']}\n", **scenario_markup) diff --git a/tests/feature/test_gherkin_terminal_reporter.py b/tests/feature/test_gherkin_terminal_reporter.py index cd558b7ab..8d246c454 100644 --- a/tests/feature/test_gherkin_terminal_reporter.py +++ b/tests/feature/test_gherkin_terminal_reporter.py @@ -351,3 +351,56 @@ def _(): result.stdout.fnmatch_lines("*Scenario: Scenario 2*") result.stdout.fnmatch_lines("*Rule: Rule 2*") result.stdout.fnmatch_lines("*Example: Example 3*") + + +FEATURE_WITH_TAGS = """\ +@feature-tag +Feature: Tagged feature + @scenario-tag-1 @scenario-tag-2 + Scenario: Tagged scenario + Given there is a bar + When the bar is accessed + Then world explodes +""" + +TEST_WITH_TAGS = """\ +from pytest_bdd import given, when, then, scenario + + +@given('there is a bar') +def _(): + return 'bar' + +@when('the bar is accessed') +def _(): + pass + + +@then('world explodes') +def _(): + pass + + +@scenario('test.feature', 'Tagged scenario') +def test_tagged_scenario(): + pass + +""" + + +def test_verbose_mode_should_display_tags(pytester): + pytester.makefile(".feature", test=FEATURE_WITH_TAGS) + pytester.makepyfile(TEST_WITH_TAGS) + result = pytester.runpytest("--gherkin-terminal-reporter", "-v") + result.assert_outcomes(passed=1, failed=0) + result.stdout.fnmatch_lines("*Feature: Tagged feature @feature-tag*") + result.stdout.fnmatch_lines("*Scenario: Tagged scenario @scenario-tag-1 @scenario-tag-2*") + + +def test_double_verbose_mode_should_display_tags(pytester): + pytester.makefile(".feature", test=FEATURE_WITH_TAGS) + pytester.makepyfile(TEST_WITH_TAGS) + result = pytester.runpytest("--gherkin-terminal-reporter", "-vv") + result.assert_outcomes(passed=1, failed=0) + result.stdout.fnmatch_lines("*Feature: Tagged feature @feature-tag*") + result.stdout.fnmatch_lines("*Scenario: Tagged scenario @scenario-tag-1 @scenario-tag-2*")