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
1 change: 1 addition & 0 deletions changelog/14329.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Now the markers are considered in the order from near to far, through the mro. `get_closes_marker("usefixtures")` will also return the nearest mark, but the processing of the `usefixtures` goes from the far mark to the near one.
4 changes: 3 additions & 1 deletion src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -1677,7 +1677,9 @@ def _getautousenames(self, node: nodes.Node) -> Iterator[str]:

def _getusefixturesnames(self, node: nodes.Item) -> Iterator[str]:
"""Return the names of usefixtures fixtures applicable to node."""
for marker_node, mark in node.iter_markers_with_node(name="usefixtures"):
for marker_node, mark in reversed(
list(node.iter_markers_with_node(name="usefixtures"))
):
if not mark.args:
marker_node.warn(
PytestWarning(
Expand Down
4 changes: 1 addition & 3 deletions src/_pytest/mark/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,9 +432,7 @@ def get_unpacked_marks(
if not consider_mro:
mark_lists = [obj.__dict__.get("pytestmark", [])]
else:
mark_lists = [
x.__dict__.get("pytestmark", []) for x in reversed(obj.__mro__)
]
mark_lists = [x.__dict__.get("pytestmark", []) for x in obj.__mro__]
mark_list = []
for item in mark_lists:
if isinstance(item, list):
Expand Down
39 changes: 38 additions & 1 deletion testing/test_mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,43 @@ def test_has_inherited(self):
assert has_inherited_marker.kwargs == {"location": "class"}
assert has_own.get_closest_marker("missing") is None

def test_mark_closest_with_parent(self, pytester: Pytester):
test_path = pytester.makepyfile(
"""
import pytest

@pytest.mark.data(0)
class TestParent:
def get_data_mark(self, request: pytest.FixtureRequest):
getted_mark = request.node.get_closest_marker("data")
assert getted_mark
return getted_mark

def test_case(self, request: pytest.FixtureRequest):
data_mark = self.get_data_mark(request)
assert data_mark.args[0] == 0

@pytest.mark.data(1)
def test_case_with_own_mark(self, request: pytest.FixtureRequest):
data_mark = self.get_data_mark(request)
assert data_mark.args[0] == 1


@pytest.mark.data(2)
class TestChild(TestParent):
def test_case(self, request: pytest.FixtureRequest):
data_mark = self.get_data_mark(request)
assert data_mark.args[0] == 2

@pytest.mark.data(3)
def test_case_with_own_mark(self, request: pytest.FixtureRequest):
data_mark = self.get_data_mark(request)
assert data_mark.args[0] == 3
"""
)
result = pytester.runpytest(test_path)
result.assert_outcomes(passed=4)

def test_mark_with_wrong_marker(self, pytester: Pytester) -> None:
reprec = pytester.inline_runsource(
"""
Expand Down Expand Up @@ -1229,7 +1266,7 @@ class C(A, B):

all_marks = get_unpacked_marks(C)

assert all_marks == [xfail("b").mark, xfail("a").mark, xfail("c").mark]
assert all_marks == [xfail("c").mark, xfail("a").mark, xfail("b").mark]

assert get_unpacked_marks(C, consider_mro=False) == [xfail("c").mark]

Expand Down
Loading