Skip to content
Open
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
6 changes: 6 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,32 @@ class Folders:
ENVS = {
# python 3.14
(PY314, "pytest-latest"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": ""}},
(PY314, "pytest8.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<9"}},
(PY314, "pytest7.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<8"}},
(PY314, "pytest6.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<7"}},
# python 3.13
(PY313, "pytest-latest"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": ""}},
(PY313, "pytest8.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<9"}},
(PY313, "pytest7.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<8"}},
(PY313, "pytest6.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<7"}},
# python 3.12
(PY312, "pytest-latest"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": ""}},
(PY312, "pytest8.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<9"}},
(PY312, "pytest7.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<8"}},
(PY312, "pytest6.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<7"}},
# python 3.11
# We'll run 'pytest-latest' this last for coverage
(PY311, "pytest8.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<9"}},
(PY311, "pytest7.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<8"}},
(PY311, "pytest6.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<7"}},
# python 3.10
(PY310, "pytest-latest"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": ""}},
(PY310, "pytest8.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<9"}},
(PY310, "pytest7.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<8"}},
(PY310, "pytest6.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<7"}},
# python 3.9
(PY39, "pytest-latest"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": ""}},
(PY39, "pytest8.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<9"}},
(PY39, "pytest7.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<8"}},
(PY39, "pytest6.x"): {"coverage": False, "pkg_specs": {"pip": ">19", "pytest": "<7"}},
# IMPORTANT: this should be last so that the folder docs/reports is not deleted afterwards
Expand Down
1 change: 1 addition & 0 deletions src/pytest_cases/common_pytest_marks.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
PYTEST8_OR_GREATER = PYTEST_VERSION >= Version('8.0.0')
PYTEST81_OR_GREATER = PYTEST_VERSION >= Version('8.1.0')
PYTEST84_OR_GREATER = PYTEST_VERSION >= Version('8.4.0')
PYTEST9_OR_GREATER = PYTEST_VERSION >= Version('9.0.0')


def get_param_argnames_as_list(argnames):
Expand Down
17 changes: 14 additions & 3 deletions src/pytest_cases/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@

from .common_mini_six import string_types
from .common_pytest_lazy_values import get_lazy_args
from .common_pytest_marks import PYTEST35_OR_GREATER, PYTEST46_OR_GREATER, PYTEST37_OR_GREATER, PYTEST7_OR_GREATER, PYTEST8_OR_GREATER
from .common_pytest_marks import PYTEST35_OR_GREATER, PYTEST46_OR_GREATER, PYTEST37_OR_GREATER, PYTEST7_OR_GREATER, \
PYTEST8_OR_GREATER, PYTEST9_OR_GREATER
from .common_pytest import get_pytest_nodeid, get_pytest_function_scopeval, is_function_node, get_param_names, \
get_param_argnames_as_list, has_function_scope, set_callspec_arg_scope_to_function, in_callspec_explicit_args

Expand Down Expand Up @@ -334,8 +335,18 @@ def _build_closure(self,
# normal fixture
self.add_required_fixture(fixname, fixturedefs)

# add all dependencies in the to do list
dependencies = _fixdef.argnames
# add all dependencies, accounting for overrides
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to make a test case that would affect the equivalent on line 322 so I didn't make any changes there

if PYTEST9_OR_GREATER:
dependencies = []
for _fixture_or_overridden in reversed(fixturedefs):
dependencies = list(_fixture_or_overridden.argnames) + dependencies
# If there's an override and doesn't depend on the overridden fixture,
# ignore remaining definitions
if fixname not in _fixture_or_overridden.argnames:
break
else:
dependencies = _fixdef.argnames

# - append: was pytest default
# pending_fixture_names += dependencies
# - prepend: makes much more sense
Expand Down
91 changes: 91 additions & 0 deletions tests/cases/issues/test_issue_374.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
OVERRIDDEN_FIXTURES_TEST_FILE = """
import pytest
@pytest.fixture
def db(): pass
@pytest.fixture
def app(db): pass
# See https://github.com/pytest-dev/pytest/issues/13773
# Issue occurred in collection with Pytest 9+
class TestOverrideWithParent:
# Overrides module-level app, doesn't request `db` directly, only transitively.
@pytest.fixture
def app(self, app): pass
def test_something(self, app): pass
class TestOverrideWithoutParent:
# Overrides module-level app, doesn't request `db` at all.
@pytest.fixture
def app(self): pass
def test_something(self, app): pass
"""


def test_overridden_fixtures(pytester):
pytester.makepyfile(OVERRIDDEN_FIXTURES_TEST_FILE)
result = pytester.runpytest()
result.assert_outcomes(passed=2)


# Using union fixtures.
OVERRIDDEN_UNION_FIXTURES_TEST_FILE = """
import pytest
from pytest_cases import parametrize, parametrize_with_cases, case, fixture
@fixture
def db(): pass
@fixture
def app(db): pass
def case_hello():
return "hello !"
@fixture
def surname():
return "joe"
@fixture
@parametrize("_name", ["you", "earthling"])
def name(_name, surname, app):
return f"{_name} {surname}"
@case(id="hello_fixture")
def case_basic3(name):
return "hello, %s !" % name
class TestOverrideWithParent:
# Overrides module-level name, doesn't request `name` directly, only transitively.
@fixture
def name(self, name):
return "overridden %s" % name
@parametrize_with_cases("msg", cases=".")
def test_something(self, msg): pass
class TestOverrideWithoutParent:
# Overrides module-level name, doesn't request name at all
@fixture
@parametrize("_name", ["hi", "martian"])
def name(self, _name):
return _name
@parametrize_with_cases("msg", cases=".")
def test_something(self, msg): pass
"""


def test_overridden_unions(pytester):
pytester.makepyfile(OVERRIDDEN_UNION_FIXTURES_TEST_FILE)
result = pytester.runpytest()
result.assert_outcomes(passed=6)
Loading