Repository navigation
fix(e2e): resolve marker MRO mismatch and add missing model marker #569
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
204d710
53c0a35
961c98e
b01569b
789b176
04d1c12
06e6549
4edca19
976ad32
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,28 @@ | |
| import pytest | ||
|
|
||
|
|
||
| def resolve_class_marker( | ||
| item: pytest.Item, | ||
| marker_name: str, | ||
| ) -> pytest.Mark | None: | ||
| """Resolve a marker with correct MRO precedence for inherited classes. | ||
|
|
||
| Walks the class MRO (child-first) so that a child class marker overrides | ||
| a parent class marker. Falls back to ``get_closest_marker`` for | ||
| non-class (module/function-level) tests. | ||
|
|
||
| Works on pytest ``Item`` nodes (not ``FixtureRequest``). | ||
| """ | ||
| if hasattr(item, "cls") and item.cls is not None: | ||
| for cls in item.cls.__mro__: | ||
| if hasattr(cls, "pytestmark"): | ||
| markers = cls.pytestmark if isinstance(cls.pytestmark, list) else [cls.pytestmark] | ||
| for marker in markers: | ||
| if marker.name == marker_name: | ||
| return marker | ||
|
Comment on lines
+27
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed — |
||
| return item.get_closest_marker(marker_name) | ||
|
Comment on lines
+25
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preserve function-level marker precedence before class MRO lookup. At Line 25, the resolver returns a class marker before checking marks directly attached to the test item. That prevents per-test overrides (function-level marker over class marker), and the regression propagates to 🔧 Proposed fix def resolve_class_marker(
item: pytest.Item,
marker_name: str,
) -> pytest.Mark | None:
@@
+ # Keep pytest "closest" behavior for marks defined on the test item itself.
+ for marker in reversed(getattr(item, "own_markers", [])):
+ if marker.name == marker_name:
+ return marker
+
if hasattr(item, "cls") and item.cls is not None:
for cls in item.cls.__mro__:
if hasattr(cls, "pytestmark"):
markers = cls.pytestmark if isinstance(cls.pytestmark, list) else [cls.pytestmark]
for marker in markers:
if marker.name == marker_name:
return marker
return item.get_closest_marker(marker_name)🤖 Prompt for AI Agents
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed — There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: Length of output: 1101 🏁 Script executed: Length of output: 593 🏁 Script executed: Length of output: 315 🏁 Script executed: Length of output: 632 🏁 Script executed: Length of output: 4836
def resolve_class_marker(
item: pytest.Item,
marker_name: str,
) -> pytest.Mark | None:
if hasattr(item, "cls") and item.cls is not None:
for cls in item.cls.__mro__:
if hasattr(cls, "pytestmark"):
markers = cls.pytestmark if isinstance(cls.pytestmark, list) else [cls.pytestmark]
for marker in markers:
if marker.name == marker_name:
return marker # Returns class marker before checking function-level
return item.get_closest_marker(marker_name)This means function-level markers (via Could you clarify:
🏁 Script executed: Length of output: 232 🏁 Script executed: Length of output: 333 🏁 Script executed: Length of output: 735
def resolve_class_marker(
item: pytest.Item,
marker_name: str,
) -> pytest.Mark | None:
if hasattr(item, "cls") and item.cls is not None:
for cls in item.cls.__mro__:
if hasattr(cls, "pytestmark"):
markers = cls.pytestmark if isinstance(cls.pytestmark, list) else [cls.pytestmark]
for marker in markers:
if marker.name == marker_name:
return marker # Returns class marker immediately
return item.get_closest_marker(marker_name) # Only called if no class markerThe concern: This prevents function-level markers from overriding class-level markers. Example scenario: `@pytest.mark.model`("class-model")
class TestExample:
`@pytest.mark.model`("function-model") # This override is ignored
def test_something(self):
passWith the current code,
Question: Do you actually use function-level marker overrides in your test suite? If not, the current implementation may be fine for your use case. If you do need function-level overrides, we should fix this by checking function markers first (either via 🧠 Learnings used |
||
|
|
||
|
|
||
| def get_marker_value( | ||
| request: pytest.FixtureRequest, | ||
| marker_name: str, | ||
|
|
@@ -18,6 +40,9 @@ def get_marker_value( | |
| ) -> Any: | ||
| """Get a value from a pytest marker. | ||
|
|
||
| For class-based tests, walks the MRO (child-first) so that a child class | ||
| marker overrides a parent class marker. | ||
|
|
||
| Args: | ||
| request: The pytest fixture request. | ||
| marker_name: Name of the marker to look for. | ||
|
|
@@ -27,7 +52,7 @@ def get_marker_value( | |
| Returns: | ||
| The marker argument value or default. | ||
| """ | ||
| marker = request.node.get_closest_marker(marker_name) | ||
| marker = resolve_class_marker(request.node, marker_name) | ||
| if marker is None: | ||
| return default | ||
| if marker.args and len(marker.args) > arg_index: | ||
|
|
@@ -42,6 +67,9 @@ def get_marker_kwargs( | |
| ) -> dict[str, Any]: | ||
| """Get keyword arguments from a pytest marker. | ||
|
|
||
| For class-based tests, walks the MRO (child-first) so that a child class | ||
| marker overrides a parent class marker. | ||
|
|
||
| Args: | ||
| request: The pytest fixture request. | ||
| marker_name: Name of the marker to look for. | ||
|
|
@@ -51,7 +79,7 @@ def get_marker_kwargs( | |
| Dict of keyword arguments merged with defaults. | ||
| """ | ||
| result = dict(defaults) if defaults else {} | ||
| marker = request.node.get_closest_marker(marker_name) | ||
| marker = resolve_class_marker(request.node, marker_name) | ||
| if marker is not None: | ||
| result.update(marker.kwargs) | ||
| return result | ||
Uh oh!
There was an error while loading. Please reload this page.