diff --git a/AUTHORS b/AUTHORS index e9e033c73f0..ff41614954e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,6 +14,7 @@ Ahn Ki-Wook Akhilesh Ramakrishnan Akiomi Kamakura Alan Velasco +Aleksandr Brodin Alessio Izzo Alex Jones Alex Lambson diff --git a/changelog/4112.feature.rst b/changelog/4112.feature.rst new file mode 100644 index 00000000000..5ce12d38e8f --- /dev/null +++ b/changelog/4112.feature.rst @@ -0,0 +1 @@ +Support use `pytest.mark.usefixtures` with `pytest.mark.parametrize`. diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 73d77f978f7..118e7b72b6b 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -163,6 +163,7 @@ def getfuncargnames( and not isinstance( inspect.getattr_static(cls, name, default=None), staticmethod ) + and not hasattr(function, "__self__") ): arg_names = arg_names[1:] # Remove any names that will be replaced with mocks. diff --git a/src/_pytest/python.py b/src/_pytest/python.py index cbb82e39090..02300a0aa64 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1799,9 +1799,13 @@ def __init__( if keywords: self.keywords.update(keywords) + fm = self.session._fixturemanager + fixtureinfo_ = fm.getfixtureinfo(self, self.obj, self.cls) if fixtureinfo is None: - fm = self.session._fixturemanager - fixtureinfo = fm.getfixtureinfo(self, self.obj, self.cls) + fixtureinfo = fixtureinfo_ + elif set(fixtureinfo_.names_closure) != set(fixtureinfo.names_closure): + fixtureinfo_.name2fixturedefs.update(fixtureinfo.name2fixturedefs) + fixtureinfo = fixtureinfo_ self._fixtureinfo: FuncFixtureInfo = fixtureinfo self.fixturenames = fixtureinfo.names_closure self._initrequest() diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 4ee9e32d6e9..fa9f445368a 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -2112,3 +2112,31 @@ def test_converted_to_str(a, b): "*= 6 passed in *", ] ) + + def test_simple_usefixtures_single_argname(self, pytester: Pytester) -> None: + pytester.makepyfile( + """ + import pytest + + @pytest.fixture + def some_fixture(): + pytest.skip() + + @pytest.mark.parametrize("val", [ + 1, + pytest.param(2, marks=pytest.mark.usefixtures('some_fixture')), + 3, + ]) + def test_foo(request, val): + assert val + """ + ) + result = pytester.runpytest("-vv", "-s") + result.assert_outcomes(passed=2, skipped=1) + result.stdout.fnmatch_lines( + [ + "test_simple_usefixtures_single_argname.py::test_foo[1] PASSED", + "test_simple_usefixtures_single_argname.py::test_foo[2] SKIPPED", + "test_simple_usefixtures_single_argname.py::test_foo[3] PASSED", + ] + )