Files
Plugin_SN_Verfahrensgebiet/tests/test_verfahrensgebiet_alkis_komplett.py
T
2026-04-22 14:53:45 +02:00

361 lines
14 KiB
Python

"""sn_verfahrensgebiet/tests/test_verfahrensgebiet_alkis_komplett.py
Unit-Tests für :mod:`sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett`.
Getestete Szenarien:
Erfolgsfall:
- Vollständiger Workflow → enriched Layer + flst_layer + LoadStatus.FIRST
- VKZ aus Projektvariable wird als Feld gesetzt (wenn vorhanden)
- Kein VKZ in Projektvariable → Feld wird nicht gesetzt (kein Fehler)
Abbruchpfade:
- verfahrensgebiet_alkis gibt LoadStatus.NONE → (None, None, NONE)
- flurstueck_alkis gibt LoadStatus.NONE → (None, None, NONE)
- Keine Flurstücke selektiert → (None, flst_layer, NONE) + Warnung
- erstelle_verfahrensgebiet_aus_auswahl liefert ungültigen Layer → (None, flst_layer, NONE)
- Cleanup: ALKIS-VG-Hilfs-Layer wird in allen Fällen aus Projekt entfernt
Hilfsfunktionen:
- _select_by_location: processing-Fallback liefert 0 → keine Exception
- _fuege_vkz_feld_hinzu: leeres VKZ wird ignoriert
"""
import sys
import types
import unittest
from unittest.mock import patch, MagicMock, call
# ---------------------------------------------------------------------------
# Minimales QGIS-Mock-Setup
# ---------------------------------------------------------------------------
if "qgis" not in sys.modules:
qgis_mod = types.ModuleType("qgis")
qgis_core = types.ModuleType("qgis.core")
qgis_utils = types.ModuleType("qgis.utils")
qgis_utils.iface = types.SimpleNamespace()
qgis_utils.plugins = {}
qgis_mod.utils = qgis_utils
qgis_mod.core = qgis_core
sys.modules["qgis"] = qgis_mod
sys.modules["qgis.core"] = qgis_core
sys.modules["qgis.utils"] = qgis_utils
from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis import LoadStatus
from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett import (
verfahrensgebiet_alkis_komplett,
_select_by_location,
_fuege_vkz_feld_hinzu,
)
# ---------------------------------------------------------------------------
# Dummies
# ---------------------------------------------------------------------------
class _DummyRect:
def xMinimum(self): return 414000.0
def yMinimum(self): return 5657000.0
def xMaximum(self): return 415000.0
def yMaximum(self): return 5658000.0
def _make_layer(valid=True, name="Layer", lid="L1", feature_count=1):
layer = MagicMock()
layer.isValid.return_value = valid
layer.id.return_value = lid
layer.name.return_value = name
layer.extent.return_value = _DummyRect()
layer.featureCount.return_value = feature_count
layer.selectedFeatureCount.return_value = feature_count
return layer
class _DummyProject:
def __init__(self):
self.added = []
self.removed = []
self._layers_by_name: dict = {}
def addMapLayer(self, layer):
self.added.append(layer)
def removeMapLayer(self, lid):
self.removed.append(lid)
def mapLayersByName(self, name):
return self._layers_by_name.get(name, [])
def instance(self):
return self
# ---------------------------------------------------------------------------
# Tests: _select_by_location
# ---------------------------------------------------------------------------
class TestSelectByLocation(unittest.TestCase):
def test_processing_not_available_returns_zero(self):
"""Wenn processing nicht importierbar ist → 0 ohne Exception."""
flst = MagicMock()
vg = MagicMock()
with patch.dict("sys.modules", {"processing": None}):
result = _select_by_location(flst, vg)
self.assertEqual(result, 0)
def test_processing_success_returns_count(self):
"""processing.run erfolgreich → selectedFeatureCount des Layers."""
flst = MagicMock()
flst.selectedFeatureCount.return_value = 7
vg = MagicMock()
mock_processing = MagicMock()
with patch.dict("sys.modules", {"processing": mock_processing}):
result = _select_by_location(flst, vg)
self.assertEqual(result, 7)
def test_processing_exception_returns_zero(self):
"""Exception in processing.run → 0 ohne Propagation."""
flst = MagicMock()
vg = MagicMock()
mock_processing = MagicMock()
mock_processing.run.side_effect = RuntimeError("Kein QGIS")
with patch.dict("sys.modules", {"processing": mock_processing}):
result = _select_by_location(flst, vg)
self.assertEqual(result, 0)
# ---------------------------------------------------------------------------
# Tests: _fuege_vkz_feld_hinzu
# ---------------------------------------------------------------------------
class TestFuegeVkzFeldHinzu(unittest.TestCase):
def test_empty_vkz_does_nothing(self):
"""Leeres VKZ → kein Zugriff auf Layer."""
layer = MagicMock()
_fuege_vkz_feld_hinzu(layer, "")
layer.dataProvider.assert_not_called()
def test_none_vkz_does_nothing(self):
"""None-VKZ → kein Zugriff auf Layer."""
layer = MagicMock()
_fuege_vkz_feld_hinzu(layer, None)
layer.dataProvider.assert_not_called()
def test_vkz_field_added(self):
"""Nicht-leeres VKZ → Provider.addAttributes wird aufgerufen."""
pr = MagicMock()
pr.addAttributes.return_value = True
pr.changeAttributeValues.return_value = True
layer = MagicMock()
layer.dataProvider.return_value = pr
layer.fields.return_value.indexFromName.return_value = 2
layer.getFeatures.return_value = []
_fuege_vkz_feld_hinzu(layer, "VF-2024-001")
pr.addAttributes.assert_called_once()
# ---------------------------------------------------------------------------
# Tests: verfahrensgebiet_alkis_komplett (Hauptworkflow)
# ---------------------------------------------------------------------------
_PATCH_VG_ALKIS = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.verfahrensgebiet_alkis"
_PATCH_GEM_BY_BBOX = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.gemarkung_schluessel_und_namen_by_bbox"
_PATCH_FLST = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.flurstueck_alkis"
_PATCH_SELECT = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett._select_by_location"
_PATCH_ERSTELLE = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.erstelle_verfahrensgebiet_aus_auswahl"
_PATCH_VKZ = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett._fuege_vkz_feld_hinzu"
_PATCH_PROJECT = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.QgsProject"
_PATCH_MSGBOX = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.QMessageBox"
_PATCH_INFO = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.info"
_PATCH_WARNING = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.warning"
_PATCH_GET_VAR = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.get_variable"
class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
def _make_project(self):
proj = _DummyProject()
mock = MagicMock()
mock.instance.return_value = proj
# Leere mapLayersByName für Cleanup
proj._layers_by_name = {"Verfahrensgebiet": [], "TempVerfahrensgebiet": []}
return mock, proj
# ------------------------------------------------------------------ #
# 1. VG-ALKIS liefert NONE → sofortiger Abbruch #
# ------------------------------------------------------------------ #
@patch(_PATCH_PROJECT)
@patch(_PATCH_VG_ALKIS)
def test_vg_alkis_none_aborts(self, mock_vg, mock_proj):
mock_proj.instance.return_value = _DummyProject()
mock_vg.return_value = (None, LoadStatus.NONE)
enriched, flst, status = verfahrensgebiet_alkis_komplett(None)
self.assertIsNone(enriched)
self.assertIsNone(flst)
self.assertEqual(status, LoadStatus.NONE)
# ------------------------------------------------------------------ #
# 2. flurstueck_alkis liefert NONE → Abbruch nach Gemarkungsabfrage #
# ------------------------------------------------------------------ #
@patch(_PATCH_PROJECT)
@patch(_PATCH_FLST)
@patch(_PATCH_GEM_BY_BBOX)
@patch(_PATCH_VG_ALKIS)
def test_flst_none_aborts(self, mock_vg, mock_gem, mock_flst, mock_proj):
mock_proj.instance.return_value = _DummyProject()
vg_layer = _make_layer(name="Verfahrensgebiet")
mock_vg.return_value = (vg_layer, LoadStatus.FIRST)
mock_gem.return_value = [("1234", "Muster")]
mock_flst.return_value = (None, LoadStatus.NONE)
enriched, flst, status = verfahrensgebiet_alkis_komplett(None)
self.assertIsNone(enriched)
self.assertIsNone(flst)
self.assertEqual(status, LoadStatus.NONE)
# ------------------------------------------------------------------ #
# 3. Keine Selektion → Warnung + (None, flst_layer, NONE) #
# ------------------------------------------------------------------ #
@patch(_PATCH_MSGBOX)
@patch(_PATCH_SELECT)
@patch(_PATCH_PROJECT)
@patch(_PATCH_FLST)
@patch(_PATCH_GEM_BY_BBOX)
@patch(_PATCH_VG_ALKIS)
def test_no_selection_returns_none_enriched(
self, mock_vg, mock_gem, mock_flst, mock_proj, mock_select, mock_msgbox
):
mock_proj.instance.return_value = _DummyProject()
vg_layer = _make_layer(name="Verfahrensgebiet")
flst_layer = _make_layer(name="Flurstücke")
mock_vg.return_value = (vg_layer, LoadStatus.FIRST)
mock_gem.return_value = []
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
mock_select.return_value = 0 # keine Selektion
enriched, flst, status = verfahrensgebiet_alkis_komplett(None)
self.assertIsNone(enriched)
self.assertIs(flst, flst_layer)
self.assertEqual(status, LoadStatus.NONE)
mock_msgbox.warning.assert_called_once()
# ------------------------------------------------------------------ #
# 4. Vollständiger Erfolgsfall #
# ------------------------------------------------------------------ #
@patch(_PATCH_VKZ)
@patch(_PATCH_GET_VAR)
@patch(_PATCH_ERSTELLE)
@patch(_PATCH_SELECT)
@patch(_PATCH_PROJECT)
@patch(_PATCH_FLST)
@patch(_PATCH_GEM_BY_BBOX)
@patch(_PATCH_VG_ALKIS)
def test_success_full_workflow(
self,
mock_vg, mock_gem, mock_flst, mock_proj,
mock_select, mock_erstelle, mock_get_var, mock_vkz_fn,
):
"""Vollständiger Workflow → enriched Layer + flst Layer + FIRST."""
proj_instance = _DummyProject()
mock_proj.instance.return_value = proj_instance
vg_layer = _make_layer(name="Verfahrensgebiet")
flst_layer = _make_layer(name="Flurstücke", lid="FLST1")
enriched = _make_layer(name="Verfahrensgebiet", lid="VG_ENRICHED", valid=True)
mock_vg.return_value = (vg_layer, LoadStatus.FIRST)
mock_gem.return_value = [("1234", "Muster")]
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
mock_select.return_value = 3
mock_erstelle.return_value = (enriched, 3, 0)
mock_get_var.return_value = "VF-2024-001"
result_enriched, result_flst, status = verfahrensgebiet_alkis_komplett(None)
self.assertIs(result_enriched, enriched)
self.assertIs(result_flst, flst_layer)
self.assertEqual(status, LoadStatus.FIRST)
mock_vkz_fn.assert_called_once_with(enriched, "VF-2024-001")
# ------------------------------------------------------------------ #
# 5. Kein VKZ → _fuege_vkz_feld_hinzu mit leerem String #
# ------------------------------------------------------------------ #
@patch(_PATCH_VKZ)
@patch(_PATCH_GET_VAR)
@patch(_PATCH_ERSTELLE)
@patch(_PATCH_SELECT)
@patch(_PATCH_PROJECT)
@patch(_PATCH_FLST)
@patch(_PATCH_GEM_BY_BBOX)
@patch(_PATCH_VG_ALKIS)
def test_no_vkz_variable_passes_empty_string(
self,
mock_vg, mock_gem, mock_flst, mock_proj,
mock_select, mock_erstelle, mock_get_var, mock_vkz_fn,
):
proj_instance = _DummyProject()
mock_proj.instance.return_value = proj_instance
vg_layer = _make_layer(name="Verfahrensgebiet")
flst_layer = _make_layer(name="Flurstücke")
enriched = _make_layer(name="Verfahrensgebiet", valid=True)
mock_vg.return_value = (vg_layer, LoadStatus.FIRST)
mock_gem.return_value = []
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
mock_select.return_value = 2
mock_erstelle.return_value = (enriched, 2, 0)
mock_get_var.return_value = "" # keine Projektvariable
verfahrensgebiet_alkis_komplett(None)
mock_vkz_fn.assert_called_once_with(enriched, "")
# ------------------------------------------------------------------ #
# 6. erstelle_verfahrensgebiet liefert ungültigen Layer #
# ------------------------------------------------------------------ #
@patch(_PATCH_MSGBOX)
@patch(_PATCH_ERSTELLE)
@patch(_PATCH_SELECT)
@patch(_PATCH_PROJECT)
@patch(_PATCH_FLST)
@patch(_PATCH_GEM_BY_BBOX)
@patch(_PATCH_VG_ALKIS)
def test_invalid_enriched_layer_returns_none(
self, mock_vg, mock_gem, mock_flst, mock_proj, mock_select, mock_erstelle, mock_msgbox
):
mock_proj.instance.return_value = _DummyProject()
vg_layer = _make_layer()
flst_layer = _make_layer(name="Flurstücke")
bad_enriched = _make_layer(valid=False)
mock_vg.return_value = (vg_layer, LoadStatus.FIRST)
mock_gem.return_value = []
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
mock_select.return_value = 5
mock_erstelle.return_value = (bad_enriched, 0, 5)
enriched, flst, status = verfahrensgebiet_alkis_komplett(None)
self.assertIsNone(enriched)
self.assertIs(flst, flst_layer)
self.assertEqual(status, LoadStatus.NONE)
mock_msgbox.critical.assert_called_once()
if __name__ == "__main__":
unittest.main()