From: Pieter Lenaerts <plenae@disroot.org>
Date: Wed, 10 Jun 2026 22:29:36 +0200
Subject: Patch mock usage in test_commander.py

Fix unittest mock conflicts by replacing class-level @patch decorator with
setUp()/addCleanup pattern, storing the mock in self.ClassMock.
Class-level @patch doesn't work properly with unittest.TestCase.
Forwarded: https://github.com/uebelack/python-maxcube-api/pull/50
---
 tests/test_commander.py | 44 +++++++++++++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 17 deletions(-)

diff --git a/tests/test_commander.py b/tests/test_commander.py
index 33fbf8b..cbf2c67 100644
--- a/tests/test_commander.py
+++ b/tests/test_commander.py
@@ -19,25 +19,33 @@ Q_CMD = Message("q")
 TEST_TIMEOUT = Timeout("test", 1.0)
 
 
-@patch("maxcube.commander.Connection", spec=True)
 class TestCommander(TestCase):
     """ Test Max! Cube command handler """
 
-    def init(self, ClassMock):
-        self.connection = ClassMock.return_value
+    def setUp(self):
+        patcher = patch("maxcube.commander.Connection", spec=True)
+        self.addCleanup(patcher.stop)
+        self.ClassMock = patcher.start()
+        self.connection = self.ClassMock.return_value
         self.commander = Commander("host", 1234)
 
-    def testDisconnectIsNoopIfAlreadyDisconnected(self, ClassMock):
+    def init(self, ClassMock):
+        # For backwards compatibility, but prefer setUp
+        if ClassMock is not None:
+            self.connection = ClassMock.return_value
+            self.commander = Commander("host", 1234)
+
+    def testDisconnectIsNoopIfAlreadyDisconnected(self, ClassMock=None):
         self.init(ClassMock)
         self.commander.disconnect()
 
-        ClassMock.assert_not_called()
+        self.ClassMock.assert_not_called()
 
         self.connection.send.assert_not_called()
         self.connection.recv.assert_not_called()
         self.connection.close.assert_not_called()
 
-    def testUpdateOpensNewNonPersistantConnectionAndClosesIt(self, ClassMock):
+    def testUpdateOpensNewNonPersistantConnectionAndClosesIt(self, ClassMock=None):
         messages = [Message("H"), Message("L")]
         self.init(ClassMock)
         self.commander.use_persistent_connection = False
@@ -49,7 +57,7 @@ class TestCommander(TestCase):
         self.assertEqual(2, self.connection.recv.call_count)
         self.connection.close.assert_called_once()
 
-    def testUpdateSendsCommandAfterAfterTimeout(self, ClassMock):
+    def testUpdateSendsCommandAfterAfterTimeout(self, ClassMock=None):
         messages = [Message("H"), None]
         self.init(ClassMock)
         self.connection.recv.side_effect = messages
@@ -70,6 +78,8 @@ class TestCommander(TestCase):
         self.connection.close.assert_not_called()
 
     def testSendRadioMsgAutoconnects(self, ClassMock=None):
+        if ClassMock is None:
+            ClassMock = self.ClassMock
         self.init(ClassMock)
         self.connection.recv.side_effect = [
             L_CMD_SUCCESS,  # connection preamble
@@ -96,8 +106,8 @@ class TestCommander(TestCase):
         self.assertEqual(2, self.connection.recv.call_count)
         self.connection.close.assert_called_once()
 
-    def testSendRadioMsgReusesConnection(self, ClassMock):
-        self.testSendRadioMsgAutoconnects()
+    def testSendRadioMsgReusesConnection(self, ClassMock=None):
+        self.testSendRadioMsgAutoconnects(None)
 
         self.connection.send.reset_mock()
         self.connection.recv.reset_mock()
@@ -110,19 +120,19 @@ class TestCommander(TestCase):
         self.connection.close.assert_not_called()
 
     def testSendRadioMsgClosesConnectionOnErrorAndRetriesIfReusingConnection(
-        self, ClassMock
+        self, ClassMock=None
     ):
-        self.testSendRadioMsgAutoconnects()
+        self.testSendRadioMsgAutoconnects(None)
 
         self.connection.recv.reset_mock()
+        self.connection.send.reset_mock()
         self.connection.recv.side_effect = [
             None,  # First read before first try
         ]
-        self.connection.send.reset_mock()
         self.connection.send.side_effect = [OSError]
 
-        newConnection = MagicMock(Connection)
-        ClassMock.side_effect = [newConnection]
+        newConnection = MagicMock()
+        self.ClassMock.side_effect = [newConnection]
         newConnection.recv.side_effect = [
             L_CMD_SUCCESS,  # Connection preamble
             S_CMD_SUCCESS,
@@ -143,7 +153,7 @@ class TestCommander(TestCase):
             deadlineMock.return_value = deadline
             return self.commander.send_radio_msg(msg)
 
-    def testSendRadioMsgShouldNotRetryOnErrorWhenConnectionIsNew(self, ClassMock):
+    def testSendRadioMsgShouldNotRetryOnErrorWhenConnectionIsNew(self, ClassMock=None):
         self.init(ClassMock)
         self.connection.recv.side_effect = [L_CMD_SUCCESS]
         self.connection.send.side_effect = [OSError]
@@ -158,7 +168,7 @@ class TestCommander(TestCase):
         self.connection.recv.assert_called_once()
         self.connection.close.assert_called_once()
 
-    def testSendRadioMsgFailsOnLogicalError(self, ClassMock):
+    def testSendRadioMsgFailsOnLogicalError(self, ClassMock=None):
         self.init(ClassMock)
         self.connection.recv.side_effect = [L_CMD_SUCCESS, S_CMD_ERROR]
 
@@ -172,7 +182,7 @@ class TestCommander(TestCase):
         self.connection.close.assert_not_called()
         deadline.remaining.assert_not_called()
 
-    def testSendRadioMsgRetriesOnThrottlingError(self, ClassMock):
+    def testSendRadioMsgRetriesOnThrottlingError(self, ClassMock=None):
         self.init(ClassMock)
         self.connection.recv.side_effect = [L_CMD_SUCCESS, S_CMD_THROTTLE_ERROR]
 
