Repository: pytest-dev/pytest-order (language: python)

Issue:
Allow more than one order marker per test
Currently, only one order marker per test is allowed. This does not matter if the markers are directly on the test, as the attributes can always be written i the same marker, but it does if there are both class and test markers shall be used, e.g.:
```py
@pytest.mark.order(after="TestClass2")
class TestClass1:
    @pytest.mark.order(2)
    def test_1():
        pass

    @pytest.mark.order(1)
    def test_2():
        pass
```
will currently not work, because the class marker will be ignored if a local test marker is present.


Discussion / hints:
This is actually a bug - the documentation notes that class markers work, so they also have to work with test markers.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ff55d8..e210d33 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # pytest-order Release Notes
 
+## Unreleased
+
+### Fixes
+- correctly handle combined class and test order markers,
+  see [#45](https://github.com/pytest-dev/pytest-order/issues/45)
+
 ## [Version 1.0.0](https://pypi.org/project/pytest-order/1.0.0/) (2021-05-30)
 First Python 3 only version.
 


Test patch:
diff --git a/pytest_order/__init__.py b/pytest_order/__init__.py
index 5becc17..4930cac 100644
--- a/pytest_order/__init__.py
+++ b/pytest_order/__init__.py
@@ -1,1 +1,1 @@
-__version__ = "1.0.0"
+__version__ = "1.1.dev0"
diff --git a/pytest_order/sorter.py b/pytest_order/sorter.py
index 722d2bf..c9e34cd 100644
--- a/pytest_order/sorter.py
+++ b/pytest_order/sorter.py
@@ -128,7 +128,7 @@ def mark_binning(
         if has_dependency or self.settings.auto_mark_dep:
             self.handle_dependency_mark(item, has_order, dep_marks, aliases)
         if has_order:
-            self.handle_order_mark(item)
+            self.handle_order_marks(item)
 
     def handle_dependency_mark(
         self,
@@ -160,8 +160,12 @@ def handle_dependency_mark(
             name_mark = item.node_id
         aliases[name_mark] = item
 
-    def handle_order_mark(self, item: Item) -> None:
-        mark = cast(Mark, item.item.get_closest_marker("order"))
+    def handle_order_marks(self, item: Item) -> None:
+        marks = item.item.iter_markers("order")
+        for mark in marks:
+            self.handle_order_mark(item, mark)
+
+    def handle_order_mark(self, item: Item, mark: Mark) -> None:
         order = mark.args[0] if mark.args else mark.kwargs.get("index")
         if order is not None:
             if isinstance(order, int):
@@ -171,7 +175,8 @@ def handle_order_mark(self, item: Item) -> None:
             else:
                 warn("Unknown order attribute:'{}'".format(order))
                 order = None
-        item.order = order
+        if item.order is None:
+            item.order = order
         self.handle_relative_marks(item, mark)
         if order is not None:
             item.nr_rel_items = 0
diff --git a/tests/test_class_marks.py b/tests/test_class_marks.py
index 4d6d491..1303418 100644
--- a/tests/test_class_marks.py
+++ b/tests/test_class_marks.py
@@ -179,3 +179,30 @@ def test_2(self): pass
         "Test1::test_1",
         "Test1::test_2",
     ]
+
+
+def test_rel_class_mark_with_order_mark(item_names_for):
+    tests_content = (
+        """
+        import pytest
+
+        class Test1:
+            def test_1(self): pass
+
+            def test_2(self): pass
+
+        @pytest.mark.order(before="Test1")
+        class Test2:
+            @pytest.mark.order(2)
+            def test_1(self): pass
+
+            @pytest.mark.order(1)
+            def test_2(self): pass
+        """
+    )
+    assert item_names_for(tests_content) == [
+        "Test2::test_2",
+        "Test2::test_1",
+        "Test1::test_1",
+        "Test1::test_2",
+    ]
diff --git a/tests/test_order_group_scope_relative.py b/tests/test_order_group_scope_relative.py
index 19177dd..3eb8152 100644
--- a/tests/test_order_group_scope_relative.py
+++ b/tests/test_order_group_scope_relative.py
@@ -133,3 +133,34 @@ def test_class_group_scope_module_scope(fixture_path):
         "test_rel4.py::test_one PASSED",
         "test_rel4.py::test_two PASSED",
     ])
+
+
+def test_rel_class_mark_with_order_mark(test_path):
+    test_path.makepyfile(
+        test_class_rel="""
+        import pytest
+
+        class Test1:
+            def test_1(self): pass
+
+            @pytest.mark.order(1)
+            def test_2(self): pass
+
+        @pytest.mark.order(before="Test1")
+        class Test2:
+            def test_1(self): pass
+
+            @pytest.mark.order(1)
+            def test_2(self): pass
+        """
+    )
+    result = test_path.runpytest(
+        "-v", "--order-group-scope=class"
+    )
+    result.assert_outcomes(passed=4, failed=0)
+    result.stdout.fnmatch_lines([
+        "test_class_rel.py::Test2::test_2 PASSED",
+        "test_class_rel.py::Test2::test_1 PASSED",
+        "test_class_rel.py::Test1::test_2 PASSED",
+        "test_class_rel.py::Test1::test_1 PASSED",
+    ])
diff --git a/tests/test_relative_ordering.py b/tests/test_relative_ordering.py
index d1a5f97..71d4719 100644
--- a/tests/test_relative_ordering.py
+++ b/tests/test_relative_ordering.py
@@ -322,6 +322,28 @@ def test_3():
     assert item_names_for(test_content) == ["test_3", "test_2", "test_1"]
 
 
+def test_multiple_markers(item_names_for):
+    test_content = (
+        """
+        import pytest
+
+        def test_1():
+            pass
+
+        @pytest.mark.order(before="test_1")
+        @pytest.mark.order(2)
+        def test_2():
+            pass
+
+        @pytest.mark.order(1)
+        @pytest.mark.order(before="test_1")
+        def test_3():
+            pass
+        """
+    )
+    assert item_names_for(test_content) == ["test_3", "test_2", "test_1"]
+
+
 def test_combined_markers3(item_names_for):
     test_content = (
         """