From 28ac968a95fce236f33f391f550883cc90b03478 Mon Sep 17 00:00:00 2001 From: Mikhail Golikov Date: Sat, 9 May 2026 15:12:25 +0100 Subject: [PATCH] Fix empty docstring delivery to step functions (#809) ScenarioTemplate.steps_from_template_steps used a truthy check (`if step.docstring`) when building the step list. An empty docstring is a valid value, but `""` is falsy, so the docstring was replaced with None. The step then looked like it had no docstring at all, and pytest fell back to fixture lookup, raising "fixture 'docstring' not found". Switched the check to `is not None` so empty docstrings are passed through as `""`. Added a regression test in tests/steps/test_docstring.py. --- CHANGES.rst | 1 + src/pytest_bdd/parser.py | 2 +- tests/steps/test_docstring.py | 50 +++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3b94132cb..64b0edfa9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -30,6 +30,7 @@ Removed Fixed +++++ * Made type annotations stronger and removed most of the ``typing.Any`` usages and ``# type: ignore`` annotations. `#658 `_ +* Empty docstrings are now correctly forwarded to step functions as an empty string instead of being silently dropped, which previously caused pytest to report a missing ``docstring`` fixture. `#809 `_ Security ++++++++ diff --git a/src/pytest_bdd/parser.py b/src/pytest_bdd/parser.py index e2be84829..a39d152ab 100644 --- a/src/pytest_bdd/parser.py +++ b/src/pytest_bdd/parser.py @@ -217,7 +217,7 @@ def render(self, context: Mapping[str, object]) -> Scenario: line_number=step.line_number, keyword=step.keyword, datatable=step.render_datatable(step.datatable, context) if step.datatable else None, - docstring=render_string(step.docstring, context) if step.docstring else None, + docstring=render_string(step.docstring, context) if step.docstring is not None else None, ) for step in base_steps ] diff --git a/tests/steps/test_docstring.py b/tests/steps/test_docstring.py index c2e3abc19..901a02291 100644 --- a/tests/steps/test_docstring.py +++ b/tests/steps/test_docstring.py @@ -197,6 +197,56 @@ def _(): result.assert_outcomes(passed=1) +def test_steps_with_empty_docstring(pytester): + """Regression test for #809. + + An empty docstring must be passed to the step function as an empty + string. Before the fix it was dropped, and pytest then treated + ``docstring`` as a missing fixture. + """ + pytester.makefile( + ".feature", + empty_docstring=textwrap.dedent( + '''\ + Feature: Empty docstring + + Scenario: Step receives an empty docstring + Given a step has an empty docstring + """ + """ + ''' + ), + ) + pytester.makeconftest( + textwrap.dedent( + r""" + from pytest_bdd import given + from pytest_bdd.utils import dump_obj + + + @given("a step has an empty docstring") + def _(docstring): + dump_obj(docstring) + """ + ) + ) + pytester.makepyfile( + textwrap.dedent( + """\ + from pytest_bdd import scenarios + + scenarios("empty_docstring.feature") + """ + ) + ) + + result = pytester.runpytest("-s") + result.assert_outcomes(passed=1) + + docstrings = collect_dumped_objects(result) + assert docstrings == [""] + + def test_docstring_step_argument_is_reserved_and_cannot_be_used(pytester): pytester.makefile( ".feature",