Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
18 changes: 15 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,19 @@ 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_set = set()
for _fixture_or_overridden in reversed(fixturedefs):
dependencies_set.update(_fixture_or_overridden.argnames)
# 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
dependencies = list(dependencies_set)
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