Skip to content
Closed
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
14 changes: 14 additions & 0 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
from _pytest.deprecated import PARSEFACTORIES_NODEID_DEPRECATED
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 @@ -1898,10 +1899,23 @@ 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 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

# In the common case we only look at the fixture def with the
# closest scope (last in the list). But if the fixture overrides
# another fixture, while requesting the super fixture, keep going
Expand Down
33 changes: 33 additions & 0 deletions testing/python/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -4970,6 +4970,39 @@ def test_indirect(arg2):
result.stdout.fnmatch_lines(["*::test_indirect[[]1[]]*"])


def test_indirect_parametrize_overrides_fixture_params(pytester: Pytester) -> None:
"""Indirect parametrization should override fixture params (#14591)."""
pytester.makepyfile(
"""
import pytest

class MyFixture:
def __init__(self, mode):
self.mode = mode

@pytest.fixture(params=['first_mode', 'second_mode'])
def myfixture(request):
yield MyFixture(request.param)

def test_myfixture_default(myfixture):
assert isinstance(myfixture, MyFixture)
assert myfixture.mode in {'first_mode', 'second_mode'}

@pytest.mark.parametrize('myfixture', ['first_mode'], indirect=True)
def test_myfixture_single_mode1(myfixture):
assert isinstance(myfixture, MyFixture)
assert myfixture.mode == 'first_mode'

@pytest.mark.parametrize('myfixture', ['second_mode'], indirect=True)
def test_myfixture_single_mode2(myfixture):
assert isinstance(myfixture, MyFixture)
assert myfixture.mode == 'second_mode'
"""
)
result = pytester.runpytest("-v")
result.assert_outcomes(passed=4)


def test_fixture_named_request(pytester: Pytester) -> None:
pytester.copy_example("fixtures/test_fixture_named_request.py")
result = pytester.runpytest()
Expand Down
Loading