Repository: Open-MSS/MSS
Issue category: Performance Issue

Issue:
What to do with "UserWarning: An unhandled message box popped up during your test!"?
There are many of these warnings in the CI logs basically spamming the output and drowning out other more interesting warnings.

These warnings are originating from https://github.com/Open-MSS/MSS/blob/1327ede1dbe3f4eb26bf3889934fa76c74fb428b/conftest.py#L227.

I am not sure why this warning was introduced in the first place, but in my opinion there are two options to handle this better:
1. If it is an issue that there are unhandled message boxes, then this should fail the respective test.
2. If it is not an issue, then we shouldn't flood the output with this warning.

Since I am not too familiar with the internals of the test suite I'd like to discuss how to make this better.
What to do with "UserWarning: An unhandled message box popped up during your test!"?
There are many of these warnings in the CI logs basically spamming the output and drowning out other more interesting warnings.

These warnings are originating from https://github.com/Open-MSS/MSS/blob/1327ede1dbe3f4eb26bf3889934fa76c74fb428b/conftest.py#L227.

I am not sure why this warning was introduced in the first place, but in my opinion there are two options to handle this better:
1. If it is an issue that there are unhandled message boxes, then this should fail the respective test.
2. If it is not an issue, then we shouldn't flood the output with this warning.

Since I am not too familiar with the internals of the test suite I'd like to discuss how to make this better.


Hints:
see here, the warning comes from the fixture
https://github.com/Open-MSS/MSS/blob/develop/conftest.py#L214

tests better should fail instead of hiding one cause, some of the tests showing that have to do a second turn. 

Sometimes functionality gets added but not the test improved e.g.

```
call(<mslib.msui.mscolab.MSColab_ConnectDialog object at 0x7fc4c26e3e20>, 'Update Credentials', 'You are using new credentials. Should your settings file be updated with the new credentials?', <PyQt5.QtWidgets.QMessageBox.StandardButtons object at 0x7fc4bfefa880>, 65536), call().__eq__(16384)]
688
    warnings.warn(f"An unhandled message box popped up during your test!\n{summary}")
  
````

If that had failed then we would know better.




see here, the warning comes from the fixture
https://github.com/Open-MSS/MSS/blob/develop/conftest.py#L214

tests better should fail instead of hiding one cause, some of the tests showing that have to do a second turn. 

Sometimes functionality gets added but not the test improved e.g.

```
call(<mslib.msui.mscolab.MSColab_ConnectDialog object at 0x7fc4c26e3e20>, 'Update Credentials', 'You are using new credentials. Should your settings file be updated with the new credentials?', <PyQt5.QtWidgets.QMessageBox.StandardButtons object at 0x7fc4bfefa880>, 65536), call().__eq__(16384)]
688
    warnings.warn(f"An unhandled message box popped up during your test!\n{summary}")
  
````

If that had failed then we would know better.





Patch:
diff --git a/.github/workflows/python-flake8.yml b/.github/workflows/python-flake8.yml
index b578708e4..0e9003135 100644
--- a/.github/workflows/python-flake8.yml
+++ b/.github/workflows/python-flake8.yml
@@ -19,10 +19,10 @@ jobs:
     timeout-minutes: 10
     steps:
     - uses: actions/checkout@v3
-    - name: Set up Python 3.8
+    - name: Set up Python 3.10
       uses: actions/setup-python@v3
       with:
-        python-version: 3.8
+        python-version: "3.10"
     - name: Lint with flake8
       run: |
         python -m pip install --upgrade pip
diff --git a/mslib/msui/mscolab.py b/mslib/msui/mscolab.py
index a6a2f0662..2795391e0 100644
--- a/mslib/msui/mscolab.py
+++ b/mslib/msui/mscolab.py
@@ -872,8 +872,11 @@ def add_operation(self):
             self.logout()
         else:
             if r.text == "True":
-                self.error_dialog = QtWidgets.QErrorMessage()
-                self.error_dialog.showMessage('Your operation was created successfully')
+                QtWidgets.QMessageBox.information(
+                    self.ui,
+                    "Creation successful",
+                    "Your operation was created successfully.",
+                )
                 op_id = self.get_recent_op_id()
                 self.new_op_id = op_id
                 self.conn.handle_new_operation(op_id)
@@ -1130,8 +1133,11 @@ def change_category_handler(self):
                 if r.text == "True":
                     self.active_operation_category = entered_operation_category
                     self.reload_operation_list()
-                    self.error_dialog = QtWidgets.QErrorMessage()
-                    self.error_dialog.showMessage("Description is updated successfully.")
+                    QtWidgets.QMessageBox.information(
+                        self.ui,
+                        "Update successful",
+                        "Category is updated successfully.",
+                    )
                 else:
                     show_popup(self.ui, "Error", "Your Connection is expired. New Login required!")
                     self.logout()
@@ -1166,8 +1172,11 @@ def change_description_handler(self):
                     self.set_operation_desc_label(entered_operation_desc)
 
                     self.reload_operation_list()
-                    self.error_dialog = QtWidgets.QErrorMessage()
-                    self.error_dialog.showMessage("Description is updated successfully.")
+                    QtWidgets.QMessageBox.information(
+                        self.ui,
+                        "Update successful",
+                        "Description is updated successfully.",
+                    )
                 else:
                     show_popup(self.ui, "Error", "Your Connection is expired. New Login required!")
                     self.logout()
@@ -1207,8 +1216,11 @@ def rename_operation_handler(self):
                     # Update other user's operation list
                     self.conn.signal_operation_list_updated.connect(self.reload_operation_list)
 
-                    self.error_dialog = QtWidgets.QErrorMessage()
-                    self.error_dialog.showMessage("Operation is renamed successfully.")
+                    QtWidgets.QMessageBox.information(
+                        self.ui,
+                        "Rename successful",
+                        "Operation is renamed successfully.",
+                    )
                 else:
                     show_popup(self.ui, "Error", "Your Connection is expired. New Login required!")
                     self.logout()
@@ -1928,6 +1940,12 @@ def logout(self):
             return
         self.ui.local_active = True
         self.ui.menu_handler()
+
+        # disconnect socket
+        if self.conn is not None:
+            self.conn.disconnect()
+            self.conn = None
+
         # close all hanging window
         self.close_external_windows()
         self.hide_operation_options()
@@ -1960,10 +1978,6 @@ def logout(self):
         self.ui.activeOperationDesc.setText(self.ui.tr("Select Operation to View Description."))
         # set usernameLabel back to default
         self.ui.usernameLabel.setText("User")
-        # disconnect socket
-        if self.conn is not None:
-            self.conn.disconnect()
-            self.conn = None
         # Turn off work locally toggle
         self.ui.workLocallyCheckbox.blockSignals(True)
         self.ui.workLocallyCheckbox.setChecked(False)
@@ -1986,11 +2000,9 @@ def logout(self):
 
         self.operation_archive_browser.hide()
 
-        # Don't try to activate local flighttrack while testing
-        if "pytest" not in sys.modules:
-            # activate first local flighttrack after logging out
-            self.ui.listFlightTracks.setCurrentRow(0)
-            self.ui.activate_selected_flight_track()
+        # activate first local flighttrack after logging out
+        self.ui.listFlightTracks.setCurrentRow(0)
+        self.ui.activate_selected_flight_track()
 
 
 class MscolabMergeWaypointsDialog(QtWidgets.QDialog, merge_wp_ui.Ui_MergeWaypointsDialog):
diff --git a/mslib/msui/socket_control.py b/mslib/msui/socket_control.py
index 7302b7a37..840081905 100644
--- a/mslib/msui/socket_control.py
+++ b/mslib/msui/socket_control.py
@@ -195,4 +195,24 @@ def save_file(self, token, op_id, content, comment=None):
             self.signal_reload.emit(op_id)
 
     def disconnect(self):
+        # Get all pyqtSignals defined in this class and disconnect them from all slots
+        allSignals = {
+            attr
+            for attr in dir(self.__class__)
+            if isinstance(getattr(self.__class__, attr), QtCore.pyqtSignal)
+        }
+        inheritedSignals = {
+            attr
+            for base_class in self.__class__.__bases__
+            for attr in dir(base_class)
+            if isinstance(getattr(base_class, attr), QtCore.pyqtSignal)
+        }
+        signals = {getattr(self, signal) for signal in allSignals - inheritedSignals}
+        for signal in signals:
+            try:
+                signal.disconnect()
+            except TypeError:
+                # The disconnect call can fail if there are no connected slots, so catch that error here
+                pass
+
         self.sio.disconnect()
diff --git a/mslib/utils/airdata.py b/mslib/utils/airdata.py
index b7ae556d2..e105147b6 100644
--- a/mslib/utils/airdata.py
+++ b/mslib/utils/airdata.py
@@ -252,7 +252,7 @@ def get_airspaces(countries=None):
                                             for data in airspace_data["polygon"].split(",")]
                 _airspaces.append(airspace_data)
                 _airspaces_mtime[file] = os.path.getmtime(os.path.join(OSDIR, "downloads", "aip", file))
-            else:
-                QtWidgets.QMessageBox.information(None, "No Airspaces data in file:", f"{file}")
+        else:
+            QtWidgets.QMessageBox.information(None, "No Airspaces data in file:", f"{file}")
 
     return _airspaces


Test patch:
diff --git a/conftest.py b/conftest.py
index be546d782..83f33ca85 100644
--- a/conftest.py
+++ b/conftest.py
@@ -211,9 +211,8 @@ def _load_module(module_name, path):
 
 
 @pytest.fixture(autouse=True)
-def close_open_windows():
-    """
-    Closes all windows after every test
+def fail_if_open_message_boxes_left():
+    """Fail a test if there are any Qt message boxes left open at the end
     """
     # Mock every MessageBox widget in the test suite to avoid unwanted freezes on unhandled error popups etc.
     with mock.patch("PyQt5.QtWidgets.QMessageBox.question") as q, \
@@ -224,7 +223,7 @@ def close_open_windows():
         if any(box.call_count > 0 for box in [q, i, c, w]):
             summary = "\n".join([f"PyQt5.QtWidgets.QMessageBox.{box()._extract_mock_name()}: {box.mock_calls[:-1]}"
                                  for box in [q, i, c, w] if box.call_count > 0])
-            warnings.warn(f"An unhandled message box popped up during your test!\n{summary}")
+            pytest.fail(f"An unhandled message box popped up during your test!\n{summary}")
 
 
     # Try to close all remaining widgets after each test
diff --git a/tests/_test_msui/test_mscolab.py b/tests/_test_msui/test_mscolab.py
index 7308986c2..14a8c254c 100644
--- a/tests/_test_msui/test_mscolab.py
+++ b/tests/_test_msui/test_mscolab.py
@@ -38,7 +38,7 @@
 from mslib.mscolab.models import Permission, User
 from mslib.msui.flighttrack import WaypointsTableModel
 from PyQt5 import QtCore, QtTest, QtWidgets
-from mslib.utils.config import read_config_file, config_loader
+from mslib.utils.config import read_config_file, config_loader, modify_config_file
 from tests.utils import mscolab_start_server, create_msui_settings_file, ExceptionMock
 from mslib.msui import msui
 from mslib.msui import mscolab
@@ -64,6 +64,7 @@ def setup_method(self):
         QtTest.QTest.qWait(500)
         self.application = QtWidgets.QApplication(sys.argv)
         self.main_window = msui.MSUIMainWindow(mscolab_data_dir=mscolab_settings.MSCOLAB_DATA_DIR)
+        self.main_window.create_new_flight_track()
         self.main_window.show()
         self.window = mscolab.MSColab_ConnectDialog(parent=self.main_window, mscolab=self.main_window.mscolab)
         self.window.urlCb.setEditText(self.url)
@@ -122,6 +123,7 @@ def test_disconnect(self):
 
     def test_login(self):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
         self._login(self.userdata[0], self.userdata[2])
         QtWidgets.QApplication.processEvents()
         # show logged in widgets
@@ -132,9 +134,31 @@ def test_login(self):
         # test operation listing visibility
         assert self.main_window.listOperationsMSC.model().rowCount() == 1
 
+    @mock.patch("PyQt5.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.Yes)
+    def test_login_with_different_account_shows_update_credentials_popup(self, mockbox):
+        self._connect_to_mscolab()
+        connect_window = self.main_window.mscolab.connect_window
+        self._login(self.userdata[0], self.userdata[2])
+        QtWidgets.QApplication.processEvents()
+        mockbox.assert_called_once_with(
+            connect_window,
+            "Update Credentials",
+            "You are using new credentials. Should your settings file be updated with the new credentials?",
+            mock.ANY,
+            mock.ANY,
+        )
+        # show logged in widgets
+        assert self.main_window.usernameLabel.text() == self.userdata[1]
+        assert self.main_window.connectBtn.isVisible() is False
+        assert self.main_window.mscolab.connect_window is None
+        assert self.main_window.local_active is True
+        # test operation listing visibility
+        assert self.main_window.listOperationsMSC.model().rowCount() == 1
+
     def test_logout_action_trigger(self):
         # Login
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
         self._login(self.userdata[0], self.userdata[2])
         QtWidgets.QApplication.processEvents()
         assert self.main_window.usernameLabel.text() == self.userdata[1]
@@ -149,6 +173,7 @@ def test_logout_action_trigger(self):
     def test_logout(self):
         # Login
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
         self._login(self.userdata[0], self.userdata[2])
         QtWidgets.QApplication.processEvents()
         assert self.main_window.usernameLabel.text() == self.userdata[1]
@@ -163,6 +188,7 @@ def test_logout(self):
     @mock.patch("PyQt5.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.Yes)
     def test_add_user(self, mockmessage):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
         assert config_loader(dataset="MSS_auth").get(self.url) == "something@something.org"
         assert mslib.utils.auth.get_password_from_keyring("MSCOLAB",
@@ -197,6 +223,7 @@ def test_add_users_with_updating_credentials_in_config_file(self, mockmessage):
         assert config_loader(dataset="MSS_auth").get(self.url) == "something@something.org"
         self._connect_to_mscolab()
         assert self.window.mscolab_server_url is not None
+        modify_config_file({"MSS_auth": {self.url: "anand@something.org"}})
         self._create_user("anand", "anand@something.org", "anand_pass")
         # check changed settings
         assert config_loader(dataset="MSS_auth").get(self.url) == "anand@something.org"
@@ -277,6 +304,7 @@ def setup_method(self):
         QtTest.QTest.qWait(500)
         self.application = QtWidgets.QApplication(sys.argv)
         self.window = msui.MSUIMainWindow(mscolab_data_dir=mscolab_settings.MSCOLAB_DATA_DIR)
+        self.window.create_new_flight_track()
         self.window.show()
 
     def teardown_method(self):
@@ -296,6 +324,7 @@ def teardown_method(self):
 
     def test_activate_operation(self):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
         self._login(emailid=self.userdata[0], password=self.userdata[2])
         # activate a operation
         self._activate_operation_at_index(0)
@@ -305,6 +334,7 @@ def test_activate_operation(self):
     @mock.patch("PyQt5.QtWidgets.QMessageBox")
     def test_view_open(self, mockbox):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
         self._login(emailid=self.userdata[0], password=self.userdata[2])
         # test after activating operation
         self._activate_operation_at_index(0)
@@ -338,6 +368,7 @@ def test_view_open(self, mockbox):
                               "Flight track (*.ftml)"))
     def test_handle_export(self, mockbox):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
         self._login(emailid=self.userdata[0], password=self.userdata[2])
         self._activate_operation_at_index(0)
         self.window.actionExportFlightTrackFTML.trigger()
@@ -362,6 +393,7 @@ def test_import_file(self, mockbox, ext):
         with mock.patch("PyQt5.QtWidgets.QFileDialog.getSaveFileName", return_value=(file_path, None)):
             with mock.patch("PyQt5.QtWidgets.QFileDialog.getOpenFileName", return_value=(file_path, None)):
                 self._connect_to_mscolab()
+                modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
                 self._login(emailid=self.userdata[0], password=self.userdata[2])
                 self._activate_operation_at_index(0)
                 exported_wp = WaypointsTableModel(waypoints=self.window.mscolab.waypoints_model.waypoints)
@@ -393,6 +425,7 @@ def test_import_file(self, mockbox, ext):
     @pytest.mark.skip("Runs in a timeout locally > 60s")
     def test_work_locally_toggle(self):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
         self._login(emailid=self.userdata[0], password=self.userdata[2])
         self._activate_operation_at_index(0)
         self.window.workLocallyCheckbox.setChecked(True)
@@ -413,6 +446,7 @@ def test_work_locally_toggle(self):
     @mock.patch("mslib.msui.mscolab.get_open_filename", return_value=os.path.join(sample_path, u"example.ftml"))
     def test_browse_add_operation(self, mockopen, mockmessage):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
         assert self.window.listOperationsMSC.model().rowCount() == 0
         self.window.actionAddOperation.trigger()
@@ -436,59 +470,75 @@ def test_browse_add_operation(self, mockopen, mockmessage):
         assert item.operation_path == "example"
         assert item.access_level == "creator"
 
-    @mock.patch("PyQt5.QtWidgets.QErrorMessage")
-    def test_add_operation(self, mockbox):
+    def test_add_operation(self):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
         assert self.window.usernameLabel.text() == 'something'
         assert self.window.connectBtn.isVisible() is False
-        self._create_operation("Alpha", "Description Alpha")
-        assert mockbox.return_value.showMessage.call_count == 1
-        with mock.patch("PyQt5.QtWidgets.QLineEdit.text", return_value=None):
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok) as m:
+            self._create_operation("Alpha", "Description Alpha")
+            m.assert_called_once_with(
+                self.window,
+                "Creation successful",
+                "Your operation was created successfully.",
+            )
+        with (mock.patch("PyQt5.QtWidgets.QLineEdit.text", return_value=None),
+              mock.patch("PyQt5.QtWidgets.QErrorMessage.showMessage") as m):
             self._create_operation("Alpha2", "Description Alpha")
-        with mock.patch("PyQt5.QtWidgets.QTextEdit.toPlainText", return_value=None):
+            m.assert_called_once_with("Path can't be empty")
+        with (mock.patch("PyQt5.QtWidgets.QTextEdit.toPlainText", return_value=None),
+              mock.patch("PyQt5.QtWidgets.QErrorMessage.showMessage") as m):
             self._create_operation("Alpha3", "Description Alpha")
-        self._create_operation("/", "Description Alpha")
-        assert mockbox.return_value.showMessage.call_count == 4
+            m.assert_called_once_with("Description can't be empty")
+        with mock.patch("PyQt5.QtWidgets.QErrorMessage.showMessage") as m:
+            self._create_operation("/", "Description Alpha")
+            m.assert_called_once_with("Path can't contain spaces or special characters")
         assert self.window.listOperationsMSC.model().rowCount() == 1
-        self._create_operation("reproduce-test", "Description Test")
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok) as m:
+            self._create_operation("reproduce-test", "Description Test")
+            m.assert_called_once()
         assert self.window.listOperationsMSC.model().rowCount() == 2
         self._activate_operation_at_index(0)
         assert self.window.mscolab.active_operation_name == "Alpha"
         self._activate_operation_at_index(1)
         assert self.window.mscolab.active_operation_name == "reproduce-test"
 
-    @mock.patch("PyQt5.QtWidgets.QMessageBox.information")
     @mock.patch("PyQt5.QtWidgets.QInputDialog.getText", return_value=("flight7", True))
-    def test_handle_delete_operation(self, mocktext, mockbox):
+    def test_handle_delete_operation(self, mocktext):
         # pytest.skip('needs a review for the delete button pressed. Seems to delete a None operation')
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "berta@something.org"}})
         self._create_user("berta", "berta@something.org", "something")
         assert self.window.usernameLabel.text() == 'berta'
         assert self.window.connectBtn.isVisible() is False
         assert self.window.listOperationsMSC.model().rowCount() == 0
         operation_name = "flight7"
-        self._create_operation(operation_name, "Description flight7")
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok) as m:
+            self._create_operation(operation_name, "Description flight7")
+            m.assert_called_once()
         # check for operation dir is created on server
         assert os.path.isdir(os.path.join(mscolab_settings.MSCOLAB_DATA_DIR, operation_name))
         self._activate_operation_at_index(0)
         op_id = self.window.mscolab.get_recent_op_id()
         assert op_id is not None
         assert self.window.listOperationsMSC.model().rowCount() == 1
-        self.window.actionDeleteOperation.trigger()
-        QtWidgets.QApplication.processEvents()
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok) as m:
+            self.window.actionDeleteOperation.trigger()
+            QtWidgets.QApplication.processEvents()
+            m.assert_called_once_with(self.window, "Success", 'Operation "flight7" was deleted!')
         op_id = self.window.mscolab.get_recent_op_id()
         assert op_id is None
         QtWidgets.QApplication.processEvents()
         QtTest.QTest.qWait(0)
         # check operation dir name removed
         assert os.path.isdir(os.path.join(mscolab_settings.MSCOLAB_DATA_DIR, operation_name)) is False
-        assert mockbox.call_count == 1
 
     @mock.patch("PyQt5.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.Yes)
     def test_handle_leave_operation(self, mockmessage):
         self._connect_to_mscolab()
 
+        modify_config_file({"MSS_auth": {self.url: self.userdata3[0]}})
         self._login(self.userdata3[0], self.userdata3[2])
         QtWidgets.QApplication.processEvents()
         assert self.window.usernameLabel.text() == self.userdata3[1]
@@ -514,55 +564,68 @@ def test_handle_leave_operation(self, mockmessage):
         assert self.window.listViews.count() == 0
         assert self.window.listOperationsMSC.model().rowCount() == 0
 
-    @mock.patch("PyQt5.QtWidgets.QMessageBox.information")
     @mock.patch("PyQt5.QtWidgets.QInputDialog.getText", return_value=("new_name", True))
-    def test_handle_rename_operation(self, mockbox, mockpatch):
+    def test_handle_rename_operation(self, mocktext):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
-        self._create_operation("flight1234", "Description flight1234")
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok) as m:
+            self._create_operation("flight1234", "Description flight1234")
+            m.assert_called_once()
         assert self.window.listOperationsMSC.model().rowCount() == 1
         self._activate_operation_at_index(0)
         assert self.window.mscolab.active_op_id is not None
-        self.window.actionRenameOperation.trigger()
-        QtWidgets.QApplication.processEvents()
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok) as m:
+            self.window.actionRenameOperation.trigger()
+            QtWidgets.QApplication.processEvents()
+            m.assert_called_once_with(self.window, "Rename successful", "Operation is renamed successfully.")
         QtTest.QTest.qWait(0)
         assert self.window.mscolab.active_op_id is not None
         assert self.window.mscolab.active_operation_name == "new_name"
 
-    @mock.patch("PyQt5.QtWidgets.QMessageBox.information")
-    @mock.patch("PyQt5.QtWidgets.QInputDialog.getText", return_value=("new_desciption", True))
-    def test_update_description(self, mockbox, mockpatch):
+    @mock.patch("PyQt5.QtWidgets.QInputDialog.getText", return_value=("new_description", True))
+    def test_update_description(self, mocktext):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
-        self._create_operation("flight1234", "Description flight1234")
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok) as m:
+            self._create_operation("flight1234", "Description flight1234")
+            m.assert_called_once()
         assert self.window.listOperationsMSC.model().rowCount() == 1
         self._activate_operation_at_index(0)
         assert self.window.mscolab.active_op_id is not None
-        self.window.actionChangeDescription.trigger()
-        QtWidgets.QApplication.processEvents()
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok) as m:
+            self.window.actionChangeDescription.trigger()
+            QtWidgets.QApplication.processEvents()
+            m.assert_called_once_with(self.window, "Update successful", "Description is updated successfully.")
         QtTest.QTest.qWait(0)
         assert self.window.mscolab.active_op_id is not None
-        assert self.window.mscolab.active_operation_description == "new_desciption"
+        assert self.window.mscolab.active_operation_description == "new_description"
 
-    @mock.patch("PyQt5.QtWidgets.QMessageBox.information")
     @mock.patch("PyQt5.QtWidgets.QInputDialog.getText", return_value=("new_category", True))
-    def test_update_category(self, mockbox, mockpatch):
+    def test_update_category(self, mocktext):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
-        self._create_operation("flight1234", "Description flight1234")
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok) as m:
+            self._create_operation("flight1234", "Description flight1234")
+            m.assert_called_once()
         assert self.window.listOperationsMSC.model().rowCount() == 1
         assert self.window.mscolab.active_operation_category == "example"
         self._activate_operation_at_index(0)
         assert self.window.mscolab.active_op_id is not None
-        self.window.actionChangeCategory.trigger()
-        QtWidgets.QApplication.processEvents()
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok) as m:
+            self.window.actionChangeCategory.trigger()
+            QtWidgets.QApplication.processEvents()
+            m.assert_called_once_with(self.window, "Update successful", "Category is updated successfully.")
         QtTest.QTest.qWait(0)
         assert self.window.mscolab.active_op_id is not None
         assert self.window.mscolab.active_operation_category == "new_category"
 
     @mock.patch("PyQt5.QtWidgets.QMessageBox.information")
-    def test_any_special_category(self, mockpatch):
+    def test_any_special_category(self, mockbox):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
         self._create_operation("flight1234", "Description flight1234")
         QtTest.QTest.qWait(0)
@@ -580,8 +643,10 @@ def test_any_special_category(self, mockpatch):
                             range(self.window.mscolab.ui.listOperationsMSC.count())]
         assert ["flight5678"] == operation_pathes
 
-    def test_get_recent_op_id(self):
+    @mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok)
+    def test_get_recent_op_id(self, mockbox):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "anton@something.org"}})
         self._create_user("anton", "anton@something.org", "something")
         QtTest.QTest.qWait(100)
         assert self.window.usernameLabel.text() == 'anton'
@@ -594,8 +659,10 @@ def test_get_recent_op_id(self):
         # ToDo fix number after cleanup initial data
         assert self.window.mscolab.get_recent_op_id() == current_op_id + 2
 
-    def test_get_recent_operation(self):
+    @mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok)
+    def test_get_recent_operation(self, mockbox):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "berta@something.org"}})
         self._create_user("berta", "berta@something.org", "something")
         QtTest.QTest.qWait(100)
         assert self.window.usernameLabel.text() == 'berta'
@@ -607,8 +674,10 @@ def test_get_recent_operation(self):
         assert operation["path"] == "flight1234"
         assert operation["access_level"] == "creator"
 
-    def test_open_chat_window(self):
+    @mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok)
+    def test_open_chat_window(self, mockbox):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
         self._create_operation("flight1234", "Description flight1234")
         assert self.window.listOperationsMSC.model().rowCount() == 1
@@ -619,8 +688,10 @@ def test_open_chat_window(self):
         QtTest.QTest.qWait(0)
         assert self.window.mscolab.chat_window is not None
 
-    def test_close_chat_window(self):
+    @mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok)
+    def test_close_chat_window(self, mockbox):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
         self._create_operation("flight1234", "Description flight1234")
         assert self.window.listOperationsMSC.model().rowCount() == 1
@@ -631,8 +702,10 @@ def test_close_chat_window(self):
         self.window.mscolab.close_chat_window()
         assert self.window.mscolab.chat_window is None
 
-    def test_delete_operation_from_list(self):
+    @mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Ok)
+    def test_delete_operation_from_list(self, mockbox):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "other@something.org"}})
         self._create_user("other", "other@something.org", "something")
         assert self.window.usernameLabel.text() == 'other'
         assert self.window.connectBtn.isVisible() is False
@@ -646,6 +719,7 @@ def test_delete_operation_from_list(self):
     @mock.patch("PyQt5.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.Yes)
     def test_user_delete(self, mockmessage):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
         u_id = self.window.mscolab.user['id']
         self.window.mscolab.open_profile_window()
@@ -692,6 +766,7 @@ def test_create_dir_exceptions(self, mockexit, mockbox):
     @mock.patch("PyQt5.QtWidgets.QMessageBox")
     def test_profile_dialog(self, mockbox):
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: "something@something.org"}})
         self._create_user("something", "something@something.org", "something")
         self.window.mscolab.profile_action.trigger()
         QtWidgets.QApplication.processEvents()
@@ -739,8 +814,7 @@ def _reset_config_file(self):
         config_file = fs.path.combine(MSUI_CONFIG_PATH, "msui_settings.json")
         read_config_file(path=config_file)
 
-    @mock.patch("mslib.msui.mscolab.QtWidgets.QErrorMessage.showMessage")
-    def _create_operation(self, path, description, mockbox, category="example"):
+    def _create_operation(self, path, description, category="example"):
         self.window.actionAddOperation.trigger()
         QtWidgets.QApplication.processEvents()
         self.window.mscolab.add_proj_dialog.path.setText(str(path))
diff --git a/tests/_test_msui/test_mscolab_admin_window.py b/tests/_test_msui/test_mscolab_admin_window.py
index 4427d3083..348c17814 100644
--- a/tests/_test_msui/test_mscolab_admin_window.py
+++ b/tests/_test_msui/test_mscolab_admin_window.py
@@ -25,6 +25,7 @@
     limitations under the License.
 """
 import os
+import mock
 import pytest
 import sys
 
@@ -35,6 +36,7 @@
 from mslib.msui import msui
 from mslib.mscolab.mscolab import handle_db_reset
 from mslib.mscolab.seed import add_user, get_user, add_operation, add_user_to_operation
+from mslib.utils.config import modify_config_file
 
 
 PORTS = list(range(24000, 24500))
@@ -68,9 +70,11 @@ def setup_method(self):
         QtTest.QTest.qWait(500)
         self.application = QtWidgets.QApplication(sys.argv)
         self.window = msui.MSUIMainWindow(mscolab_data_dir=mscolab_settings.MSCOLAB_DATA_DIR)
+        self.window.create_new_flight_track()
         self.window.show()
         # connect and login to mscolab
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
         self._login(emailid=self.userdata[0], password=self.userdata[2])
         # activate operation and open chat window
         self._activate_operation_at_index(0)
@@ -86,6 +90,9 @@ def teardown_method(self):
             self.window.mscolab.admin_window.close()
         if self.window.mscolab.conn:
             self.window.mscolab.conn.disconnect()
+        with mock.patch("PyQt5.QtWidgets.QMessageBox.warning", return_value=QtWidgets.QMessageBox.Yes):
+            self.window.close()
+        QtWidgets.QApplication.processEvents()
         self.application.quit()
         QtWidgets.QApplication.processEvents()
         self.process.terminate()
diff --git a/tests/_test_msui/test_mscolab_merge_waypoints.py b/tests/_test_msui/test_mscolab_merge_waypoints.py
index 6ac974510..e8afee9fc 100644
--- a/tests/_test_msui/test_mscolab_merge_waypoints.py
+++ b/tests/_test_msui/test_mscolab_merge_waypoints.py
@@ -53,6 +53,7 @@ def setup_method(self):
         QtTest.QTest.qWait(500)
         self.application = QtWidgets.QApplication(sys.argv)
         self.window = msui.MSUIMainWindow(mscolab_data_dir=mscolab_settings.MSCOLAB_DATA_DIR)
+        self.window.create_new_flight_track()
         self.emailid = 'merge@alpha.org'
 
     def teardown_method(self):
diff --git a/tests/_test_msui/test_mscolab_operation.py b/tests/_test_msui/test_mscolab_operation.py
index 5ca3a48dd..ec2b769a9 100644
--- a/tests/_test_msui/test_mscolab_operation.py
+++ b/tests/_test_msui/test_mscolab_operation.py
@@ -36,6 +36,7 @@
 from mslib.msui import msui
 from mslib.mscolab.mscolab import handle_db_reset
 from mslib.mscolab.seed import add_user, get_user, add_operation, add_user_to_operation
+from mslib.utils.config import modify_config_file
 
 PORTS = list(range(22000, 22500))
 
@@ -63,9 +64,11 @@ def setup_method(self):
         QtTest.QTest.qWait(500)
         self.application = QtWidgets.QApplication(sys.argv)
         self.window = msui.MSUIMainWindow(mscolab_data_dir=mscolab_settings.MSCOLAB_DATA_DIR)
+        self.window.create_new_flight_track()
         self.window.show()
         # connect and login to mscolab
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
         self._login(self.userdata[0], self.userdata[2])
         # activate operation and open chat window
         self._activate_operation_at_index(0)
diff --git a/tests/_test_msui/test_mscolab_version_history.py b/tests/_test_msui/test_mscolab_version_history.py
index 01e48739f..a84c61aa7 100644
--- a/tests/_test_msui/test_mscolab_version_history.py
+++ b/tests/_test_msui/test_mscolab_version_history.py
@@ -36,6 +36,7 @@
 from mslib.msui import msui
 from mslib.mscolab.mscolab import handle_db_reset
 from mslib.mscolab.seed import add_user, get_user, add_operation, add_user_to_operation
+from mslib.utils.config import modify_config_file
 
 
 PORTS = list(range(20000, 20500))
@@ -56,9 +57,11 @@ def setup_method(self):
         QtTest.QTest.qWait(500)
         self.application = QtWidgets.QApplication(sys.argv)
         self.window = msui.MSUIMainWindow(mscolab_data_dir=mscolab_settings.MSCOLAB_DATA_DIR)
+        self.window.create_new_flight_track()
         self.window.show()
         # connect and login to mscolab
         self._connect_to_mscolab()
+        modify_config_file({"MSS_auth": {self.url: self.userdata[0]}})
         self._login(self.userdata[0], self.userdata[2])
         # activate operation and open chat window
         self._activate_operation_at_index(0)
diff --git a/tests/_test_msui/test_msui.py b/tests/_test_msui/test_msui.py
index 1aee9207d..cddb9c666 100644
--- a/tests/_test_msui/test_msui.py
+++ b/tests/_test_msui/test_msui.py
@@ -263,7 +263,7 @@ def test_plugin_saveas(self, save_file):
     def test_plugin_import(self, open_file):
         with mock.patch("mslib.msui.msui_mainwindow.config_loader", return_value=self.import_plugins):
             self.window.add_import_plugins("qt")
-        with mock.patch("mslib.msui.msui_mainwindow.get_open_filenames", return_value=open_file) as mockopen:
+        with mock.patch("mslib.msui.msui_mainwindow.get_open_filenames", return_value=[open_file[0]]) as mockopen:
             assert self.window.listFlightTracks.count() == 1
             assert mockopen.call_count == 0
             self.window.last_save_directory = ROOT_DIR
diff --git a/tests/_test_msui/test_satellite_dockwidget.py b/tests/_test_msui/test_satellite_dockwidget.py
index e558fcd1c..8ebbf7c84 100644
--- a/tests/_test_msui/test_satellite_dockwidget.py
+++ b/tests/_test_msui/test_satellite_dockwidget.py
@@ -61,7 +61,13 @@ def test_load(self):
         assert self.view.plot_satellite_overpass.call_count == 2
         self.view.reset_mock()
 
-    def test_load_no_file(self):
+    @mock.patch("PyQt5.QtWidgets.QMessageBox.critical")
+    def test_load_no_file(self, mockbox):
         QtTest.QTest.mouseClick(self.window.btLoadFile, QtCore.Qt.LeftButton)
         QtWidgets.QApplication.processEvents()
         assert self.window.cbSatelliteOverpasses.count() == 0
+        mockbox.assert_called_once_with(
+            self.window,
+            "Satellite Overpass Tool",
+            "ERROR:\n<class 'fs.errors.FileExpected'>\npath '' should be a file",
+        )
diff --git a/tests/_test_utils/test_airdata.py b/tests/_test_utils/test_airdata.py
index 10299f380..1545b15ab 100644
--- a/tests/_test_utils/test_airdata.py
+++ b/tests/_test_utils/test_airdata.py
@@ -204,10 +204,12 @@ def test_get_airspaces(mockbox):
 
 
 @mock.patch("mslib.utils.airdata.download_progress", _download_incomplete_airspace)
+@mock.patch("PyQt5.QtWidgets.QMessageBox.information")
 @mock.patch("PyQt5.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.Yes)
-def test_get_airspaces_missing_data(mockbox):
+def test_get_airspaces_missing_data(mockbox, infobox):
     """ We use a test file without the need for downloading to check handling """
     # update_airspace would only update after 30 days
     _cleanup_test_files()
     airspaces = get_airspaces(countries=["bg"])
     assert airspaces == []
+    infobox.assert_called_once_with(None, 'No Airspaces data in file:', 'bg_asp.xml')
diff --git a/tests/utils.py b/tests/utils.py
index 895dca650..cbd107287 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -41,6 +41,7 @@
 from mslib.mscolab.conf import mscolab_settings
 from mslib.mscolab.server import APP, initialize_managers, start_server
 from mslib.mscolab.mscolab import handle_db_init
+from mslib.utils.config import modify_config_file
 
 
 def callback_ok_image(status, response_headers):
@@ -198,6 +199,9 @@ def mscolab_start_server(all_ports, mscolab_settings=mscolab_settings, timeout=1
 
     url = f"http://localhost:{port}"
 
+    # Update mscolab URL to avoid "Update Server List" message boxes
+    modify_config_file({"default_MSCOLAB": [url]})
+
     _app = APP
     _app.config['SQLALCHEMY_DATABASE_URI'] = mscolab_settings.SQLALCHEMY_DB_URI
     _app.config['MSCOLAB_DATA_DIR'] = mscolab_settings.MSCOLAB_DATA_DIR