Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 5 additions & 24 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
from _pytest.deprecated import MARKED_FIXTURE
from _pytest.deprecated import YIELD_FIXTURE
from _pytest.main import Session
from _pytest.mark import Mark
from _pytest.mark import ParameterSet
from _pytest.mark.structures import MarkDecorator
from _pytest.outcomes import fail
Expand Down Expand Up @@ -346,11 +345,6 @@ def prune_dependency_tree(self) -> None:
working_set = set(self.initialnames)
while working_set:
argname = working_set.pop()
# Argname may be something not included in the original names_closure,
# in which case we ignore it. This currently happens with pseudo
# FixtureDefs which wrap 'get_direct_param_fixture_func(request)'.
# So they introduce the new dependency 'request' which might have
# been missing in the original tree (closure).
if argname not in closure and argname in self.names_closure:
closure.add(argname)
if argname in self.name2fixturedefs:
Expand Down Expand Up @@ -1695,26 +1689,9 @@ def sort_by_scope(arg_name: str) -> Scope:

def pytest_generate_tests(self, metafunc: Metafunc) -> None:
"""Generate new tests based on parametrized fixtures used by the given metafunc"""

def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]:
args, _ = ParameterSet._parse_parametrize_args(*mark.args, **mark.kwargs)
return args

for argname in metafunc.fixturenames:
# Get the FixtureDefs for the argname.
fixture_defs = metafunc._arg2fixturedefs.get(argname)
if not fixture_defs:
# Will raise FixtureLookupError at setup time if not parametrized somewhere
# else (e.g @pytest.mark.parametrize)
continue

# If the test itself parametrizes using this argname, give it
# precedence.
if any(
argname in get_parametrize_mark_argnames(mark)
for mark in metafunc.definition.iter_markers("parametrize")
):
continue
fixture_defs = metafunc._arg2fixturedefs.get(argname, ())

# In the common case we only look at the fixture def with the
# closest scope (last in the list). But if the fixture overrides
Expand All @@ -1733,6 +1710,10 @@ def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]:
break

# Not requesting the overridden super fixture, stop.
#
# TODO: Handle the case where the super-fixture is transitively
# requested (see #7737 and the xfail'd test
# test_override_parametrized_fixture_via_transitive_fixture).
if argname not in fixturedef.argnames:
break

Expand Down
34 changes: 34 additions & 0 deletions testing/python/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,40 @@ def test_spam(foo):
result = pytester.runpytest()
result.stdout.fnmatch_lines(["*2 passed*"])

@pytest.mark.xfail(reason="not handled currently")
def test_override_parametrized_fixture_via_transitive_fixture(
self, pytester: Pytester
) -> None:
"""Test that overriding a parametrized fixture works even the super
fixture is requested only transitively.

Regression test for #7737.
"""
pytester.makepyfile(
"""
import pytest

@pytest.fixture(params=[1, 2])
def foo(request):
return request.param

@pytest.fixture
def bar(foo):
return foo

class TestIt:
@pytest.fixture
def foo(self, bar):
return bar * 2

def test_it(self, foo):
pass
"""
)
result = pytester.runpytest()
assert result.ret == ExitCode.OK
result.assert_outcomes(passed=2)

def test_autouse_fixture_plugin(self, pytester: Pytester) -> None:
# A fixture from a plugin has no baseid set, which screwed up
# the autouse fixture handling.
Expand Down