From cbf87cfffa987e2a28ec5ccb1361e5e8756da133 Mon Sep 17 00:00:00 2001 From: FazeelUsmani Date: Thu, 6 Nov 2025 14:08:48 +0000 Subject: [PATCH] Fix #11295: Exclude parametrize pseudo-fixtures from --fixtures-per-test Previously, pytest --fixtures-per-test would display parameter names from @pytest.mark.parametrize decorators as if they were real fixtures, showing them with "no docstring available" and pointing to internal pytest code. This fix uses the existing _get_direct_parametrize_args() function to identify and exclude these pseudo-fixtures from the --fixtures-per-test output, ensuring only actual user-defined fixtures are displayed. Changes: - Modified _show_fixtures_per_test() to filter out parametrize arguments - Added regression test to verify pseudo-fixtures are excluded --- src/_pytest/fixtures.py | 9 +++- testing/python/show_fixtures_per_test.py | 54 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 27846db13a4..906fb1c3c14 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -1967,8 +1967,15 @@ def write_item(item: nodes.Item) -> None: tw.sep("-", f"fixtures used by {item.name}") # TODO: Fix this type ignore. tw.sep("-", f"({get_best_relpath(item.function)})") # type: ignore[attr-defined] + + # Exclude parametrize pseudo-fixtures. + parametrize_args = _get_direct_parametrize_args(item) + # dict key not used in loop but needed for sorting. - for _, fixturedefs in sorted(info.name2fixturedefs.items()): + for argname, fixturedefs in sorted(info.name2fixturedefs.items()): + # Skip parametrize arguments - they are not real fixtures. + if argname in parametrize_args: + continue assert fixturedefs is not None if not fixturedefs: continue diff --git a/testing/python/show_fixtures_per_test.py b/testing/python/show_fixtures_per_test.py index c860b61e21b..28802001cae 100644 --- a/testing/python/show_fixtures_per_test.py +++ b/testing/python/show_fixtures_per_test.py @@ -254,3 +254,57 @@ def test_arg1(arg1): " Docstring content that extends into a third paragraph.", ] ) + + +def test_parametrize_pseudo_fixtures_excluded(pytester: Pytester) -> None: + """Test that parametrize arguments are not shown as fixtures. + + Regression test for issue #11295. + """ + p = pytester.makepyfile( + ''' + import pytest + @pytest.fixture + def real_fixture(): + """A real fixture with a proper definition.""" + return "real_value" + + @pytest.mark.parametrize("x", [1, 2]) + def test_with_parametrize(x, real_fixture): + """Test with both a parameter and a real fixture.""" + assert x > 0 + assert real_fixture == "real_value" + + @pytest.mark.parametrize("y,z", [(1, 2), (3, 4)]) + def test_with_multiple_parametrize(y, z): + """Test with multiple parameters and no real fixtures.""" + assert y > 0 + assert z > 0 + ''' + ) + + result = pytester.runpytest("--fixtures-per-test", p) + assert result.ret == 0 + + # Verify that real_fixture is shown for test_with_parametrize + result.stdout.fnmatch_lines( + [ + "*fixtures used by test_with_parametrize*1*", + "*(test_parametrize_pseudo_fixtures_excluded.py:8)*", + "real_fixture -- test_parametrize_pseudo_fixtures_excluded.py:3", + " A real fixture with a proper definition.", + ] + ) + + # Verify that pseudo-fixtures (x, y, z) are NOT shown + # Use more specific patterns to avoid matching header lines + result.stdout.no_fnmatch_line("x -- *.py:*") + result.stdout.no_fnmatch_line("y -- *.py:*") + result.stdout.no_fnmatch_line("z -- *.py:*") + + # Verify that test_with_multiple_parametrize shows no fixtures section + # (since it only has pseudo-fixtures, which should be excluded) + output = result.stdout.str() + # Count how many times "fixtures used by test_with_multiple_parametrize" appears + # It should appear in the separator line but have no fixtures listed + assert "fixtures used by test_with_multiple_parametrize" in output