tests überarbeitet

This commit is contained in:
2026-04-20 15:36:18 +02:00
parent 9caf0e3c86
commit 72c9d6fa15
5 changed files with 548 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
"""Tests für das Plugin sn_verfahrensgebiet."""
+86
View File
@@ -0,0 +1,86 @@
import sys
import types
import unittest
from unittest.mock import patch
if "qgis" not in sys.modules:
qgis_mod = types.ModuleType("qgis")
qgis_utils = types.ModuleType("qgis.utils")
qgis_utils.iface = types.SimpleNamespace()
qgis_utils.plugins = {}
qgis_mod.utils = qgis_utils
sys.modules["qgis"] = qgis_mod
sys.modules["qgis.utils"] = qgis_utils
from sn_verfahrensgebiet.ui.dockwidget import DockWidget
class _DummyTabWidget:
def __init__(self):
self.tabs = []
def addTab(self, widget, title):
self.tabs.append((widget, title))
class _TabWithServices:
tab_title = "WithServices"
def __init__(self, _parent):
self.services = None
def set_services(self, pruefmanager=None, data_grabber=None):
self.services = (pruefmanager, data_grabber)
class _TabWithoutServices:
tab_title = "WithoutServices"
def __init__(self, _parent):
pass
class _TabRaises:
tab_title = "Broken"
def __init__(self, _parent):
raise RuntimeError("boom")
class TestDockWidget(unittest.TestCase):
@patch("sn_verfahrensgebiet.ui.dockwidget.QTabWidget", _DummyTabWidget)
def test_init_tabs_with_services_injects_available_services(self):
obj = DockWidget.__new__(DockWidget)
obj.tabs = [_TabWithServices, _TabWithoutServices]
obj.pruefmanager = object()
obj.data_grabber = object()
obj._set_widget_calls = []
obj.setWidget = lambda value: obj._set_widget_calls.append(value)
DockWidget._init_tabs_with_services(obj)
self.assertEqual(len(obj._set_widget_calls), 2)
tab_widget = obj._set_widget_calls[-1]
self.assertIsInstance(tab_widget, _DummyTabWidget)
self.assertEqual(len(tab_widget.tabs), 2)
first_tab = tab_widget.tabs[0][0]
self.assertEqual(first_tab.services, (obj.pruefmanager, obj.data_grabber))
@patch("sn_verfahrensgebiet.ui.dockwidget.error")
@patch("sn_verfahrensgebiet.ui.dockwidget.QTabWidget", _DummyTabWidget)
def test_init_tabs_with_services_reports_errors(self, mock_error):
obj = DockWidget.__new__(DockWidget)
obj.tabs = [_TabRaises]
obj.pruefmanager = object()
obj.data_grabber = object()
obj.setWidget = lambda _value: None
DockWidget._init_tabs_with_services(obj)
mock_error.assert_called_once()
if __name__ == "__main__":
unittest.main()
+121
View File
@@ -0,0 +1,121 @@
import sys
import types
import unittest
from types import SimpleNamespace
from unittest.mock import Mock, patch
if "qgis" not in sys.modules:
qgis_mod = types.ModuleType("qgis")
qgis_utils = types.ModuleType("qgis.utils")
qgis_utils.iface = types.SimpleNamespace()
qgis_utils.plugins = {}
qgis_mod.utils = qgis_utils
sys.modules["qgis"] = qgis_mod
sys.modules["qgis.utils"] = qgis_utils
from sn_verfahrensgebiet.main import Verfahrensgebiet
class _DummyIface:
def __init__(self):
self.removed = []
def mainWindow(self):
return object()
def removeDockWidget(self, dock):
self.removed.append(dock)
class _DummyDock:
def __init__(self):
self.object_name = None
self.action = None
self.deleted = False
def setObjectName(self, name):
self.object_name = name
def deleteLater(self):
self.deleted = True
class TestPluginMain(unittest.TestCase):
@patch("sn_verfahrensgebiet.main.DataGrabber")
@patch("sn_verfahrensgebiet.main.Pruefmanager")
def test_init_sets_services_and_names(self, mock_pruefmanager, mock_data_grabber):
pm = object()
dg = object()
mock_pruefmanager.return_value = pm
mock_data_grabber.return_value = dg
plugin = Verfahrensgebiet(_DummyIface())
self.assertIs(plugin.pruefmanager, pm)
self.assertIs(plugin.data_grabber, dg)
self.assertEqual(plugin.plugin_name, "Verfahrensgebiet")
self.assertEqual(plugin.dock_name, "sn_dock_verfahrensgebiet")
@patch("sn_verfahrensgebiet.main.plugins", new_callable=dict)
def test_initGui_registers_action_when_basis_ui_exists(self, mock_plugins):
iface = _DummyIface()
plugin = Verfahrensgebiet(iface)
ui = SimpleNamespace(
add_action=Mock(return_value="action_obj"),
finalize_menu_and_toolbar=Mock(),
)
mock_plugins["sn_basis"] = SimpleNamespace(ui=ui)
plugin.initGui()
self.assertEqual(plugin.action, "action_obj")
ui.add_action.assert_called_once()
ui.finalize_menu_and_toolbar.assert_called_once()
@patch("sn_verfahrensgebiet.main.plugins", new_callable=dict)
@patch("sn_verfahrensgebiet.main.DockManager.show")
@patch("sn_verfahrensgebiet.main.DockWidget")
def test_run_creates_dock_and_marks_toolbar_active(self, mock_dockwidget_cls, mock_show, mock_plugins):
iface = _DummyIface()
plugin = Verfahrensgebiet(iface)
plugin.action = "action_obj"
dock = _DummyDock()
mock_dockwidget_cls.return_value = dock
ui = SimpleNamespace(set_active_plugin=Mock())
mock_plugins["sn_basis"] = SimpleNamespace(ui=ui)
plugin.run()
self.assertIs(plugin.dockwidget, dock)
self.assertEqual(dock.object_name, "sn_dock_verfahrensgebiet")
self.assertEqual(dock.action, "action_obj")
mock_show.assert_called_once_with(dock)
ui.set_active_plugin.assert_called_once_with("action_obj")
@patch("sn_verfahrensgebiet.main.plugins", new_callable=dict)
def test_unload_removes_dock_and_action(self, mock_plugins):
iface = _DummyIface()
plugin = Verfahrensgebiet(iface)
dock = _DummyDock()
plugin.dockwidget = dock
plugin.action = "action_obj"
ui = SimpleNamespace(remove_action=Mock())
mock_plugins["sn_basis"] = SimpleNamespace(ui=ui)
plugin.unload()
self.assertIn(dock, iface.removed)
self.assertTrue(dock.deleted)
self.assertIsNone(plugin.dockwidget)
ui.remove_action.assert_called_once_with("action_obj")
self.assertIsNone(plugin.action)
if __name__ == "__main__":
unittest.main()
+184
View File
@@ -0,0 +1,184 @@
import sys
import types
import unittest
from unittest.mock import patch
if "qgis" not in sys.modules:
qgis_mod = types.ModuleType("qgis")
qgis_utils = types.ModuleType("qgis.utils")
qgis_utils.iface = types.SimpleNamespace()
qgis_utils.plugins = {}
qgis_mod.utils = qgis_utils
sys.modules["qgis"] = qgis_mod
sys.modules["qgis.utils"] = qgis_utils
from sn_verfahrensgebiet.functions import verfahrensgebiet_alkis as vg
class _DummyMessageBox:
class StandardButton:
Yes = 1
No = 2
last_critical = None
next_reply = StandardButton.No
@staticmethod
def question(_parent, _title, _message, _buttons, _default):
return _DummyMessageBox.next_reply
@staticmethod
def critical(parent, title, message):
_DummyMessageBox.last_critical = (parent, title, message)
class _DummyProject:
def __init__(self, layers=None):
self._layers = layers or []
self.removed = []
def mapLayersByName(self, _name):
return self._layers
def removeMapLayer(self, layer_id):
self.removed.append(layer_id)
class _DummyExistingLayer:
def __init__(self, layer_id="existing"):
self._id = layer_id
def id(self):
return self._id
class _DummyGeom:
def __init__(self, empty=False):
self._empty = empty
def isEmpty(self):
return self._empty
class _DummyFeature:
def __init__(self, geom=None):
self._geom = geom or _DummyGeom()
def geometry(self):
return self._geom
class _DummyTempLayer:
def __init__(self, valid=True, features=None):
self._valid = valid
self._features = features or []
def isValid(self):
return self._valid
def getFeatures(self):
return self._features
def crs(self):
class _Crs:
@staticmethod
def authid():
return "EPSG:25833"
return _Crs()
class _DummyProvider:
def addAttributes(self, _attrs):
return True
def addFeatures(self, _features):
return True
class _DummyMemoryLayer:
def __init__(self):
self._provider = _DummyProvider()
def dataProvider(self):
return self._provider
def updateFields(self):
return None
def updateExtents(self):
return None
class TestVerfahrensgebietAlkis(unittest.TestCase):
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsProject.instance")
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox)
def test_check_existing_layer_first_load(self, mock_project_instance):
mock_project_instance.return_value = _DummyProject(layers=[])
layer, status = vg._check_existing_layer(tab_widget=None)
self.assertIsNone(layer)
self.assertEqual(status, vg.LoadStatus.FIRST)
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsProject.instance")
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox)
def test_check_existing_layer_keep_existing(self, mock_project_instance):
existing = _DummyExistingLayer("L1")
mock_project_instance.return_value = _DummyProject(layers=[existing])
_DummyMessageBox.next_reply = _DummyMessageBox.StandardButton.No
layer, status = vg._check_existing_layer(tab_widget=None)
self.assertIs(layer, existing)
self.assertEqual(status, vg.LoadStatus.KEEP)
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox)
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="")
def test_verfahrensgebiet_alkis_fails_when_verfahrensnummer_missing(self, _mock_get_var):
_DummyMessageBox.last_critical = None
layer, status = vg.verfahrensgebiet_alkis(tab_widget=None)
self.assertIsNone(layer)
self.assertEqual(status, vg.LoadStatus.NONE)
self.assertIsNotNone(_DummyMessageBox.last_critical)
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis._check_existing_layer", return_value=(None, vg.LoadStatus.FIRST))
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox)
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="VKZ123")
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsVectorLayer")
def test_verfahrensgebiet_alkis_fails_on_invalid_wfs_layer(self, mock_qgsvectorlayer, _mock_get_var, _mock_check):
_DummyMessageBox.last_critical = None
mock_qgsvectorlayer.return_value = _DummyTempLayer(valid=False)
layer, status = vg.verfahrensgebiet_alkis(tab_widget=None)
self.assertIsNone(layer)
self.assertEqual(status, vg.LoadStatus.NONE)
self.assertIsNotNone(_DummyMessageBox.last_critical)
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsFeature")
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsGeometry.unaryUnion", return_value=_DummyGeom(empty=False))
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis._check_existing_layer", return_value=(None, vg.LoadStatus.FIRST))
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="VKZ123")
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsVectorLayer")
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox)
def test_verfahrensgebiet_alkis_success_returns_memory_layer(self, mock_qgsvectorlayer, _mock_get_var, _mock_check, _mock_union, mock_feature_cls):
temp_layer = _DummyTempLayer(valid=True, features=[_DummyFeature()])
memory_layer = _DummyMemoryLayer()
mock_qgsvectorlayer.side_effect = [temp_layer, memory_layer]
mock_feature = mock_feature_cls.return_value
mock_feature.setGeometry.return_value = None
mock_feature.setAttributes.return_value = None
layer, status = vg.verfahrensgebiet_alkis(tab_widget=None)
self.assertIs(layer, memory_layer)
self.assertEqual(status, vg.LoadStatus.FIRST)
if __name__ == "__main__":
unittest.main()
+156
View File
@@ -0,0 +1,156 @@
import sys
import types
import unittest
from unittest.mock import patch
if "qgis" not in sys.modules:
qgis_mod = types.ModuleType("qgis")
qgis_utils = types.ModuleType("qgis.utils")
qgis_utils.iface = types.SimpleNamespace(
mapCanvas=lambda: types.SimpleNamespace(setExtent=lambda _e: None, refresh=lambda: None)
)
qgis_utils.plugins = {}
qgis_mod.utils = qgis_utils
sys.modules["qgis"] = qgis_mod
sys.modules["qgis.utils"] = qgis_utils
from sn_verfahrensgebiet.ui.tabs.working_tab import WorkingTab, LoadStatus
class _DummyLayer:
def __init__(self, valid=True, layer_id="L1"):
self._valid = valid
self._id = layer_id
def isValid(self):
return self._valid
def id(self):
return self._id
def extent(self):
return object()
class _DummyProject:
def __init__(self):
self.added = []
def addMapLayer(self, layer):
self.added.append(layer)
class _DummyCanvas:
def __init__(self):
self.extent = None
def setExtent(self, extent):
self.extent = extent
def refresh(self):
return None
class _DummyButton:
def __init__(self):
self._enabled = True
self._text = "Aus ALKIS laden"
def setEnabled(self, enabled):
self._enabled = enabled
def isEnabled(self):
return self._enabled
def setText(self, text):
self._text = text
def text(self):
return self._text
class _DummyCombo:
def __init__(self):
self.layer = None
def setLayer(self, layer):
self.layer = layer
class TestWorkingTab(unittest.TestCase):
def _make_tab(self):
tab = WorkingTab.__new__(WorkingTab)
tab.btn_verf_alkis = _DummyButton()
tab.verf_layer_combo = _DummyCombo()
tab.haken_verf = object()
tab.setze_haken = lambda *_args, **_kwargs: None
return tab
def test_set_busy_updates_button_state(self):
tab = self._make_tab()
tab._set_busy(tab.btn_verf_alkis, True)
self.assertFalse(tab.btn_verf_alkis.isEnabled())
self.assertEqual(tab.btn_verf_alkis.text(), "Lade ...")
tab._set_busy(tab.btn_verf_alkis, False)
self.assertTrue(tab.btn_verf_alkis.isEnabled())
self.assertEqual(tab.btn_verf_alkis.text(), "Aus ALKIS laden")
@patch("sn_verfahrensgebiet.ui.tabs.working_tab.set_verfahrensgebiet_layer_id")
def test_on_verf_layer_changed_valid_sets_variable(self, mock_set_layer_id):
tab = self._make_tab()
layer = _DummyLayer(valid=True, layer_id="layer_123")
tab._on_verf_layer_changed(layer)
mock_set_layer_id.assert_called_once_with("layer_123")
@patch("sn_verfahrensgebiet.ui.tabs.working_tab.set_verfahrensgebiet_layer_id")
def test_on_verf_layer_changed_none_clears_variable(self, mock_set_layer_id):
tab = self._make_tab()
tab._on_verf_layer_changed(None)
mock_set_layer_id.assert_called_once_with("")
@patch("sn_verfahrensgebiet.ui.tabs.working_tab.success")
@patch("sn_verfahrensgebiet.ui.tabs.working_tab.apply_style")
@patch("sn_verfahrensgebiet.ui.tabs.working_tab.QgsProject.instance")
@patch("sn_verfahrensgebiet.ui.tabs.working_tab.verfahrensgebiet_alkis")
@patch("sn_verfahrensgebiet.ui.tabs.working_tab.iface")
@patch("sn_verfahrensgebiet.ui.tabs.working_tab.set_verfahrensgebiet_layer_id")
def test_handle_verf_alkis_success_path(
self,
mock_set_layer_id,
mock_iface,
mock_alkis,
mock_project_instance,
mock_apply_style,
mock_success,
):
tab = self._make_tab()
layer = _DummyLayer(valid=True, layer_id="layer_ok")
mock_alkis.return_value = (layer, LoadStatus.FIRST)
mock_project_instance.return_value = _DummyProject()
mock_iface.mapCanvas.return_value = _DummyCanvas()
tab.handle_verf_alkis()
mock_apply_style.assert_called_once()
mock_success.assert_called_once()
mock_set_layer_id.assert_called_with("layer_ok")
@patch("sn_verfahrensgebiet.ui.tabs.working_tab.set_verfahrensgebiet_layer_id")
@patch("sn_verfahrensgebiet.ui.tabs.working_tab.verfahrensgebiet_alkis", return_value=(None, LoadStatus.NONE))
def test_handle_verf_alkis_failure_clears_layer_var(self, _mock_alkis, mock_set_layer_id):
tab = self._make_tab()
tab.handle_verf_alkis()
mock_set_layer_id.assert_called_once_with("")
if __name__ == "__main__":
unittest.main()