Kategorie "vielleicht testen" in sn_basis abgeschlossen (#67)

37 neue Testfälle für die 31 verbleibenden "vielleicht testen"-
Funktionen: os_wrapper.is_case_sensitive_fs, qgiscore_wrapper.
create_buffer_layer (nur die Wächter, echte Pufferlogik importiert
qgis.core direkt), qgisui_wrapper.find_dock_widgets/add_menu/remove_menu,
DataGrabber._aggregate_results, pruef_ergebnis.dateipfad,
Navigation (add_action, finalize_menu_and_toolbar, set_active_plugin,
remove_action, remove_all), SettingsTab._build_input und PrintTab
(18 Methoden: Zustandslogik, Signal-Handling, Atlas-/Theme-Verwaltung).

PrintTab._build_ui bekommt stattdessen den Vermerk "von Test abgesehen,
weil vernachlässigbar; HD 18.09.2026" - reine Qt-Widget-Konstruktion ohne
eigenständig prüfbare Logik. Damit ist die Kategorie "vielleicht testen"
für sn_basis aufgelöst: jede Funktion ist jetzt getestet oder mit einem
begründeten Vermerk versehen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 13:06:06 +02:00
co-authored by Claude Sonnet 5
parent aa4b383a84
commit 8eb89e92a7
10 changed files with 1023 additions and 4 deletions
+1
View File
@@ -1,3 +1,4 @@
- Kategorie „vielleicht testen" abgeschlossen: 30 weitere Funktionen bekamen Tests (os_wrapper, qgiscore_wrapper, qgisui_wrapper, DataGrabber, pruef_ergebnis, Navigation, SettingsTab, PrintTab – Zustandslogik, Signal-Handling, Atlas-/Theme-Verwaltung), `PrintTab._build_ui` bekam den Vermerk „Test ausgesetzt, zunächst Refactor" (#67)
- Testkatalog-Klassifizierung in Docstrings festgehalten: 43 als „vernachlässigbar" eingestufte Funktionen tragen jetzt den Vermerk „von Test abgesehen, weil vernachlässigbar", 41 Print-Bereich-Funktionen (`print_logic.py`, `print_layout.py`, `print_layout_baufreigabe.py`) den Vermerk „Test ausgesetzt, zunächst Refactor" – keine Verhaltensänderung
- Tests für weitere zentrale Funktionen ergänzt: Verfahrensgebiet-Zustandsverwaltung, Dock-Verwaltung, Pruefmanager-/Dateipruefer-Entscheidungswege, Fundament-Wrapper (Pfade, Landkreise, Nachrichten, Layer-Existenz/-Stil, GPKG-Layer-Prüfung, QGIS-3/4-Geometriekompatibilität) und Projekteigenschaften-Tab (#65)
- Tests für die Kernpipeline ergänzt: Datenabruf (WFS/REST/OGR-URL-Bau, Raumfilter), Datenschreiber (GPKG-Schreiben, Laden, Log), LayerLoader (CRS-Transformation, Raum-/Geometriefilter) und excel_importer (Linklisten-Import) waren zuvor ohne Testdatei (#63)
+20
View File
@@ -128,6 +128,26 @@ class TestDataGrabber(unittest.TestCase):
self.assertFalse(summary.ok)
self.assertEqual(summary.aktion, "keine_validen_eintraege")
def test_aggregate_results_mit_zeilen_liefert_erfolgsergebnis(self):
"""Prüft, dass _aggregate_results bei vorhandenen Zeilen ein Erfolgsergebnis mit Zeilenzahl liefert."""
grabber = self._make_grabber()
ergebnis = grabber._aggregate_results("quelle.xlsx", {"rows": [{"a": 1}, {"b": 2}]}, [])
self.assertTrue(ergebnis.ok)
self.assertEqual(ergebnis.aktion, "ok")
self.assertEqual(ergebnis.kontext["valid_entries"], 2)
def test_aggregate_results_ohne_zeilen_liefert_keine_validen_eintraege(self):
"""Prüft, dass _aggregate_results ohne Zeilen ok=False mit Aktion 'keine_validen_eintraege' liefert."""
grabber = self._make_grabber()
ergebnis = grabber._aggregate_results("quelle.xlsx", {"rows": []}, [])
self.assertFalse(ergebnis.ok)
self.assertEqual(ergebnis.aktion, "keine_validen_eintraege")
self.assertEqual(ergebnis.kontext["eintraege_gesamt"], 0)
if __name__ == "__main__":
unittest.main()
+196
View File
@@ -0,0 +1,196 @@
"""Tests für sn_basis/ui/navigation.py::Navigation (Issue #67, ohne init_ui – s. Docstring dort)."""
import unittest
from types import SimpleNamespace
from unittest.mock import Mock, patch
from sn_basis.ui.navigation import Navigation
class _FakeQAction:
def __init__(self, text, parent=None) -> None:
self.text = text
self.parent = parent
self.tooltip = None
self.checkable = None
self.checked = None
self.triggered = SimpleNamespace(connect=Mock())
def setToolTip(self, text) -> None:
self.tooltip = text
def setCheckable(self, value: bool) -> None:
self.checkable = value
def setChecked(self, value: bool) -> None:
self.checked = value
class _FakeGroup:
def __init__(self) -> None:
self.added = []
def addAction(self, action) -> None:
self.added.append(action)
class _FakeMenuOrToolbar:
def __init__(self) -> None:
self.cleared = False
self.added = []
self.removed = []
def clear(self) -> None:
self.cleared = True
self.added = []
def addAction(self, action) -> None:
self.added.append(action)
def removeAction(self, action) -> None:
self.removed.append(action)
class AddActionTests(unittest.TestCase):
def test_ohne_plugin_group_liefert_none(self):
"""Prüft, dass add_action ohne initialisierte Aktionsgruppe None liefert und nichts anlegt."""
nav = Navigation()
ergebnis = nav.add_action("Text", callback=lambda: None)
self.assertIsNone(ergebnis)
self.assertEqual(nav.actions, [])
@patch("sn_basis.ui.navigation.get_main_window", return_value=object())
@patch("sn_basis.ui.navigation.QAction", _FakeQAction)
def test_erzeugt_action_und_fuegt_sie_gruppe_und_liste_hinzu(self, _mock_window):
"""Prüft, dass add_action eine Action erzeugt, ihr die Gruppe zuweist und sie priorisiert in der Liste ablegt."""
nav = Navigation()
nav.plugin_group = _FakeGroup()
callback = Mock()
action = nav.add_action("Mein Text", callback, tooltip="Tooltip", priority=5)
self.assertEqual(action.text, "Mein Text")
self.assertEqual(action.tooltip, "Tooltip")
self.assertTrue(action.checkable)
action.triggered.connect.assert_called_once_with(callback)
self.assertIn(action, nav.plugin_group.added)
self.assertEqual(nav.actions, [(5, action)])
class FinalizeMenuAndToolbarTests(unittest.TestCase):
def test_ohne_menu_oder_toolbar_tut_nichts(self):
"""Prüft, dass finalize_menu_and_toolbar ohne Menü oder Toolbar keine Ausnahme wirft."""
nav = Navigation()
nav.finalize_menu_and_toolbar() # darf keine Ausnahme werfen
def test_sortiert_und_fuegt_aktionen_in_prioritaetsreihenfolge_hinzu(self):
"""Prüft, dass finalize_menu_and_toolbar Aktionen nach Priorität sortiert in Menü und Toolbar einträgt."""
nav = Navigation()
nav.menu = _FakeMenuOrToolbar()
nav.toolbar = _FakeMenuOrToolbar()
nav.actions = [(20, "a2"), (10, "a1")]
nav.finalize_menu_and_toolbar()
self.assertTrue(nav.menu.cleared)
self.assertTrue(nav.toolbar.cleared)
self.assertEqual(nav.menu.added, ["a1", "a2"])
self.assertEqual(nav.toolbar.added, ["a1", "a2"])
class SetActivePluginTests(unittest.TestCase):
def test_setzt_nur_aktive_action_auf_checked(self):
"""Prüft, dass set_active_plugin nur die übergebene Action markiert, alle anderen abwählt."""
action1, action2 = _FakeQAction("a1"), _FakeQAction("a2")
nav = Navigation()
nav.actions = [(1, action1), (2, action2)]
nav.set_active_plugin(action2)
self.assertFalse(action1.checked)
self.assertTrue(action2.checked)
def test_ohne_aktive_action_setzt_alle_auf_false(self):
"""Prüft, dass set_active_plugin ohne aktive Action alle bestehenden Actions abwählt."""
action1 = _FakeQAction("a1")
nav = Navigation()
nav.actions = [(1, action1)]
nav.set_active_plugin(None) # darf keine Ausnahme werfen
self.assertFalse(action1.checked)
class RemoveActionTests(unittest.TestCase):
def test_none_action_tut_nichts(self):
"""Prüft, dass remove_action mit None keine Ausnahme wirft und die Liste unverändert lässt."""
nav = Navigation()
nav.actions = [(1, "a1")]
nav.remove_action(None)
self.assertEqual(nav.actions, [(1, "a1")])
def test_entfernt_action_aus_menu_toolbar_und_liste(self):
"""Prüft, dass remove_action die Action aus Menü, Toolbar und der internen Liste entfernt."""
nav = Navigation()
nav.menu = _FakeMenuOrToolbar()
nav.toolbar = _FakeMenuOrToolbar()
nav.actions = [(1, "a1"), (2, "a2")]
nav.remove_action("a1")
self.assertEqual(nav.menu.removed, ["a1"])
self.assertEqual(nav.toolbar.removed, ["a1"])
self.assertEqual(nav.actions, [(2, "a2")])
def test_ohne_menu_oder_toolbar_entfernt_trotzdem_aus_liste(self):
"""Prüft, dass remove_action ohne Menü/Toolbar trotzdem aus der internen Liste entfernt."""
nav = Navigation()
nav.actions = [(1, "a1")]
nav.remove_action("a1")
self.assertEqual(nav.actions, [])
class RemoveAllTests(unittest.TestCase):
@patch("sn_basis.ui.navigation.remove_toolbar")
@patch("sn_basis.ui.navigation.remove_menu")
def test_entfernt_menu_toolbar_und_setzt_zustand_zurueck(self, mock_remove_menu, mock_remove_toolbar):
"""Prüft, dass remove_all Menü und Toolbar über die Wrapper entfernt und den Navigationszustand zurücksetzt."""
nav = Navigation()
fake_menu, fake_toolbar = object(), object()
nav.menu = fake_menu
nav.toolbar = fake_toolbar
nav.actions = [(1, "a1")]
nav.plugin_group = object()
nav.remove_all()
mock_remove_menu.assert_called_once_with(fake_menu)
mock_remove_toolbar.assert_called_once_with(fake_toolbar)
self.assertIsNone(nav.menu)
self.assertIsNone(nav.toolbar)
self.assertEqual(nav.actions, [])
self.assertIsNone(nav.plugin_group)
@patch("sn_basis.ui.navigation.remove_toolbar")
@patch("sn_basis.ui.navigation.remove_menu")
def test_ohne_menu_und_toolbar_ruft_wrapper_nicht_auf(self, mock_remove_menu, mock_remove_toolbar):
"""Prüft, dass remove_all ohne vorhandenes Menü/Toolbar die Wrapper nicht aufruft, aber die Liste leert."""
nav = Navigation()
nav.actions = [(1, "a1")]
nav.remove_all()
mock_remove_menu.assert_not_called()
mock_remove_toolbar.assert_not_called()
self.assertEqual(nav.actions, [])
if __name__ == "__main__":
unittest.main()
+27
View File
@@ -0,0 +1,27 @@
"""Tests für sn_basis/functions/os_wrapper.py::is_case_sensitive_fs (Issue #67)."""
import unittest
from unittest.mock import patch
from sn_basis.functions import os_wrapper
class IsCaseSensitiveFsTests(unittest.TestCase):
def test_windows_ist_nicht_case_sensitiv(self):
"""Prüft, dass Windows als nicht case-sensitives Dateisystem gilt."""
with patch.object(os_wrapper, "IS_WINDOWS", True), patch.object(os_wrapper, "IS_MACOS", False):
self.assertFalse(os_wrapper.is_case_sensitive_fs())
def test_macos_ist_nicht_case_sensitiv(self):
"""Prüft, dass macOS als nicht case-sensitives Dateisystem gilt."""
with patch.object(os_wrapper, "IS_WINDOWS", False), patch.object(os_wrapper, "IS_MACOS", True):
self.assertFalse(os_wrapper.is_case_sensitive_fs())
def test_linux_ist_case_sensitiv(self):
"""Prüft, dass Linux als case-sensitives Dateisystem gilt."""
with patch.object(os_wrapper, "IS_WINDOWS", False), patch.object(os_wrapper, "IS_MACOS", False):
self.assertTrue(os_wrapper.is_case_sensitive_fs())
if __name__ == "__main__":
unittest.main()
+649
View File
@@ -0,0 +1,649 @@
"""Tests für sn_basis/ui/tabs/print_tab.py::PrintTab (Issue #67).
self.logic ist eine PrintLogic-Instanz (modules/print_logic.py) - die Klasse steht wegen einer
geplanten Grundüberarbeitung komplett unter "Test ausgesetzt, zunächst Refactor" (#63/#65). Wo eine
PrintTab-Methode self.logic.irgendwas(...) aufruft, wird self.logic hier durch ein Fake/Mock ersetzt;
geprüft wird nur, dass PrintTab die richtige Methode mit den richtigen Argumenten aufruft, nie das
tatsächliche PrintLogic-Verhalten.
PrintTab(QWidget) erbt von der im Mock-Modus leeren _MockWidget-Klasse (kein setLayout() usw.) - wie bei
SettingsTab/BaseDockWidget wird nie PrintTab() echt konstruiert, sondern PrintTab.__new__(PrintTab) mit
den je Test nötigen Instanzattributen von Hand.
"""
import unittest
from unittest.mock import MagicMock, call, patch
from sn_basis.functions.qt_wrapper import ArrowDown, ArrowRight
from sn_basis.ui.tabs.print_tab import (
ATLAS_ERZEUGEN_VAR,
DRUCKBEREICH_VAR,
DRUCKBEREICH_VERFAHRENSGEBIET,
DRUCKPUFFER_STANDARD_CM,
DRUCKPUFFER_VAR,
FORMFAKTOR_UI_VAR,
KARTENNAME_38,
KARTENNAME_PLACEHOLDER,
KARTENNAME_VAR,
MASSSTAB_VAR,
MASSSTAB_WIE_KARTENFENSTER,
PLOTMASSSTAB_BY_AUSWAHL,
THEMA_VAR,
THEMA_WIE_KARTENFENSTER,
DIN_STANDARD,
PrintTab,
ZIELGROESSE_UI_VAR,
)
def _neuer_tab() -> PrintTab:
return PrintTab.__new__(PrintTab)
class _FakeCombo:
def __init__(self, text: str = "") -> None:
self._text = text
self.added_items: list = []
def currentText(self) -> str:
return self._text
def setCurrentText(self, value: str) -> None:
self._text = value
def clear(self) -> None:
self.added_items = []
def addItems(self, items) -> None:
self.added_items.extend(items)
def blockSignals(self, value: bool) -> None:
pass
class _FakeCheckbox:
def __init__(self, checked: bool = False) -> None:
self._checked = checked
def isChecked(self) -> bool:
return self._checked
def setChecked(self, value: bool) -> None:
self._checked = value
class _FakeButton:
def __init__(self, text: str = "") -> None:
self._text = text
def setText(self, value: str) -> None:
self._text = value
def text(self) -> str:
return self._text
class _FakePruefmanager:
def __init__(self, frage_text_ergebnis) -> None:
self._frage_text_ergebnis = frage_text_ergebnis
self.zeige_hinweis_calls: list = []
def frage_text(self, titel, meldung, default_text=""):
return self._frage_text_ergebnis
def zeige_hinweis(self, titel, meldung) -> None:
self.zeige_hinweis_calls.append((titel, meldung))
class _FakeSignal:
def __init__(self) -> None:
self.connected: list = []
def connect(self, fn) -> None:
self.connected.append(fn)
def disconnect(self, fn) -> None:
self.connected.remove(fn)
class _FakeProjectSignals:
def __init__(self) -> None:
self.readProject = _FakeSignal()
self.newProjectCreated = _FakeSignal()
self.cleared = _FakeSignal()
class _FakeThemeCollection:
def __init__(self) -> None:
self.mapThemesChanged = _FakeSignal()
self.changed = _FakeSignal()
self.themeChanged = _FakeSignal()
class SetServicesTests(unittest.TestCase):
@patch("sn_basis.ui.tabs.print_tab.PrintLogic")
def test_ruft_alle_set_methoden_mit_combo_werten_auf(self, mock_logic_cls):
"""Prüft, dass set_services alle set_*-Methoden von PrintLogic mit den aktuellen Combo-Werten aufruft."""
tab = _neuer_tab()
tab._kartenname_combo = _FakeCombo("§38")
tab._massstab_combo = _FakeCombo("1:1000")
tab._thema_combo = _FakeCombo("Thema A")
tab._zielgroesse_combo = _FakeCombo("A3")
tab._druckbereich_combo = _FakeCombo("Verfahrensgebiet")
tab._endlosrolle_cb = _FakeCheckbox(True)
tab._get_current_canvas_scale = lambda: 5000.0
mock_logic = mock_logic_cls.return_value
pruefmanager = object()
tab.set_services(pruefmanager, object())
self.assertIs(tab.pruefmanager, pruefmanager)
mock_logic.set_kartenname_for_auswahl.assert_called_once_with("§38")
mock_logic.set_plotmassstab_for_auswahl.assert_called_once_with("1:1000", 5000.0)
mock_logic.set_view_for_auswahl.assert_called_once_with("Thema A")
mock_logic.set_zielgroesse_for_auswahl.assert_called_once_with("A3")
mock_logic.set_druckbereich_for_auswahl.assert_called_once_with("Verfahrensgebiet")
mock_logic.set_formfaktor.assert_called_once_with(True)
@patch("sn_basis.ui.tabs.print_tab.PrintLogic")
def test_ohne_combos_ruft_keine_set_methode_auf(self, mock_logic_cls):
"""Prüft, dass set_services ohne vorhandene Combos keine set_*-Methode von PrintLogic aufruft."""
tab = _neuer_tab()
tab._kartenname_combo = None
tab._massstab_combo = None
tab._thema_combo = None
tab._zielgroesse_combo = None
tab._druckbereich_combo = None
tab._endlosrolle_cb = None
mock_logic = mock_logic_cls.return_value
tab.set_services(object(), object())
mock_logic.set_kartenname_for_auswahl.assert_not_called()
mock_logic.set_plotmassstab_for_auswahl.assert_not_called()
mock_logic.set_view_for_auswahl.assert_not_called()
mock_logic.set_zielgroesse_for_auswahl.assert_not_called()
mock_logic.set_druckbereich_for_auswahl.assert_not_called()
mock_logic.set_formfaktor.assert_not_called()
self.assertIs(tab.logic, mock_logic)
class RestoreStateTests(unittest.TestCase):
def _neuer_tab_mit_widgets(self) -> PrintTab:
tab = _neuer_tab()
tab._kartenname_combo = _FakeCombo()
tab._massstab_combo = _FakeCombo()
tab._thema_combo = _FakeCombo()
tab._zielgroesse_combo = _FakeCombo()
tab._endlosrolle_cb = _FakeCheckbox()
tab._druckbereich_combo = _FakeCombo()
tab._druckpuffer_button = _FakeButton()
tab._atlas_erzeugen_cb = None
tab._atlas_layer_combo = None
tab._get_gespeicherte_themen = lambda: ["Thema A"]
return tab
def test_gueltige_gespeicherte_werte_werden_uebernommen(self):
"""Prüft, dass _restore_state gültige gespeicherte Werte in die jeweiligen Widgets übernimmt."""
tab = self._neuer_tab_mit_widgets()
massstab_gueltig = next(iter(PLOTMASSSTAB_BY_AUSWAHL.keys()))
werte = {
KARTENNAME_VAR: KARTENNAME_38,
MASSSTAB_VAR: massstab_gueltig,
THEMA_VAR: THEMA_WIE_KARTENFENSTER,
ZIELGROESSE_UI_VAR: DIN_STANDARD,
FORMFAKTOR_UI_VAR: "Blatt",
DRUCKBEREICH_VAR: DRUCKBEREICH_VERFAHRENSGEBIET,
DRUCKPUFFER_VAR: "7",
}
with patch("sn_basis.ui.tabs.print_tab.get_variable", side_effect=lambda key, scope=None: werte.get(key)):
tab._restore_state()
self.assertEqual(tab._kartenname_combo.currentText(), KARTENNAME_38)
self.assertEqual(tab._massstab_combo.currentText(), massstab_gueltig)
self.assertEqual(tab._druckbereich_combo.currentText(), DRUCKBEREICH_VERFAHRENSGEBIET)
self.assertEqual(tab._druckpuffer_button.text(), "7 cm")
def test_ungueltige_oder_fehlende_werte_fallen_auf_defaults_zurueck(self):
"""Prüft, dass _restore_state ohne (gültige) gespeicherte Werte auf die Default-Konstanten zurückfällt."""
tab = self._neuer_tab_mit_widgets()
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value=None):
tab._restore_state()
self.assertEqual(tab._kartenname_combo.currentText(), KARTENNAME_PLACEHOLDER)
self.assertEqual(tab._massstab_combo.currentText(), MASSSTAB_WIE_KARTENFENSTER)
self.assertEqual(tab._druckbereich_combo.currentText(), DRUCKBEREICH_VERFAHRENSGEBIET)
self.assertEqual(tab._druckpuffer_button.text(), f"{DRUCKPUFFER_STANDARD_CM} cm")
class OnDruckpufferClickedTests(unittest.TestCase):
def test_gueltige_zahl_setzt_variable_und_button_text(self):
"""Prüft, dass eine bestätigte, gültige Zahl den Druckpuffer speichert und den Button-Text aktualisiert."""
tab = _neuer_tab()
tab.pruefmanager = _FakePruefmanager(("5", True))
tab._druckpuffer_button = _FakeButton()
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value=None), \
patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set:
tab._on_druckpuffer_clicked()
mock_set.assert_called_once_with(DRUCKPUFFER_VAR, "5", scope="project")
self.assertEqual(tab._druckpuffer_button.text(), "5 cm")
def test_abgebrochen_aendert_nichts(self):
"""Prüft, dass ein abgebrochener Dialog den Druckpuffer nicht verändert."""
tab = _neuer_tab()
tab.pruefmanager = _FakePruefmanager(("5", False))
tab._druckpuffer_button = _FakeButton()
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value=None), \
patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set:
tab._on_druckpuffer_clicked()
mock_set.assert_not_called()
def test_ungueltige_zahl_zeigt_hinweis_und_setzt_nichts(self):
"""Prüft, dass eine nicht-numerische Eingabe einen Hinweis zeigt und den Druckpuffer nicht setzt."""
tab = _neuer_tab()
pruefmanager = _FakePruefmanager(("abc", True))
tab.pruefmanager = pruefmanager
tab._druckpuffer_button = _FakeButton()
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value=None), \
patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set:
tab._on_druckpuffer_clicked()
mock_set.assert_not_called()
self.assertEqual(len(pruefmanager.zeige_hinweis_calls), 1)
class OnVorlageErstellenTests(unittest.TestCase):
def test_ruft_druckvorlage_anlegen_mit_combo_werten_auf(self):
"""Prüft, dass _on_vorlage_erstellen druckvorlage_anlegen mit den aktuellen Combo-Werten aufruft."""
tab = _neuer_tab()
mock_logic = MagicMock()
tab.logic = mock_logic
tab.pruefmanager = object()
tab._kartenname_combo = _FakeCombo("§38")
tab._massstab_combo = _FakeCombo("1:1000")
tab._zielgroesse_combo = _FakeCombo("A3")
tab._endlosrolle_cb = _FakeCheckbox(True)
tab._druckbereich_combo = _FakeCombo("Verfahrensgebiet")
tab._on_vorlage_erstellen()
mock_logic.druckvorlage_anlegen.assert_called_once_with(
layer=None,
kartenname_auswahl="§38",
massstab_auswahl="1:1000",
zielgroesse="A3",
formfaktor=True,
druckbereich_auswahl="Verfahrensgebiet",
)
def test_ohne_logic_tut_nichts(self):
"""Prüft, dass _on_vorlage_erstellen ohne gesetzte PrintLogic keine Ausnahme wirft."""
tab = _neuer_tab()
tab.logic = None
tab.pruefmanager = object()
tab._on_vorlage_erstellen() # darf keine Ausnahme werfen
class ProjectSignalsTests(unittest.TestCase):
def test_connect_verbindet_alle_drei_signale(self):
"""Prüft, dass _connect_project_signals alle drei Projektsignale mit _on_project_changed verbindet."""
tab = _neuer_tab()
projekt = _FakeProjectSignals()
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
mock_project.instance.return_value = projekt
tab._connect_project_signals()
self.assertEqual(projekt.readProject.connected, [tab._on_project_changed])
self.assertEqual(projekt.newProjectCreated.connected, [tab._on_project_changed])
self.assertEqual(projekt.cleared.connected, [tab._on_project_changed])
def test_disconnect_trennt_alle_drei_signale(self):
"""Prüft, dass _disconnect_project_signals alle drei zuvor verbundenen Projektsignale trennt."""
tab = _neuer_tab()
projekt = _FakeProjectSignals()
projekt.readProject.connect(tab._on_project_changed)
projekt.newProjectCreated.connect(tab._on_project_changed)
projekt.cleared.connect(tab._on_project_changed)
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
mock_project.instance.return_value = projekt
tab._disconnect_project_signals()
self.assertEqual(projekt.readProject.connected, [])
self.assertEqual(projekt.newProjectCreated.connected, [])
self.assertEqual(projekt.cleared.connected, [])
def test_instance_wirft_ausnahme_wird_abgefangen(self):
"""Prüft, dass eine Ausnahme bei QgsProject.instance() in beiden Signalmethoden abgefangen wird."""
tab = _neuer_tab()
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
mock_project.instance.side_effect = RuntimeError("kaputt")
tab._connect_project_signals() # darf keine Ausnahme werfen
tab._disconnect_project_signals() # darf keine Ausnahme werfen
class CleanupTests(unittest.TestCase):
def test_ruft_beide_disconnect_methoden_auf(self):
"""Prüft, dass cleanup sowohl die Theme- als auch die Projektsignale trennt."""
tab = _neuer_tab()
tab._disconnect_theme_collection_signals = MagicMock()
tab._disconnect_project_signals = MagicMock()
tab.cleanup()
tab._disconnect_theme_collection_signals.assert_called_once()
tab._disconnect_project_signals.assert_called_once()
class ToggleAtlasTests(unittest.TestCase):
def test_checked_true_setzt_arrow_down_und_zeigt_inhalt(self):
"""Prüft, dass _toggle_atlas bei checked=True den Pfeil nach unten dreht und den Inhalt einblendet."""
tab = _neuer_tab()
tab._atlas_button = MagicMock()
tab._atlas_content = MagicMock()
tab._toggle_atlas(True)
tab._atlas_button.setArrowType.assert_called_once_with(ArrowDown)
tab._atlas_content.setVisible.assert_called_once_with(True)
def test_checked_false_setzt_arrow_right_und_versteckt_inhalt(self):
"""Prüft, dass _toggle_atlas bei checked=False den Pfeil nach rechts dreht und den Inhalt ausblendet."""
tab = _neuer_tab()
tab._atlas_button = MagicMock()
tab._atlas_content = MagicMock()
tab._toggle_atlas(False)
tab._atlas_button.setArrowType.assert_called_once_with(ArrowRight)
tab._atlas_content.setVisible.assert_called_once_with(False)
class RestoreAtlasLayerSelectionTests(unittest.TestCase):
def test_vorhandene_layer_id_setzt_layer_in_combo(self):
"""Prüft, dass eine gespeicherte Layer-ID den zugehörigen Layer im Atlas-Dropdown auswählt."""
tab = _neuer_tab()
tab._atlas_layer_combo = MagicMock(spec=["setLayer"])
fake_layer = object()
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value="layer_1"), \
patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
mock_project.instance.return_value.mapLayer.return_value = fake_layer
tab._restore_atlas_layer_selection()
tab._atlas_layer_combo.setLayer.assert_called_once_with(fake_layer)
def test_leere_gespeicherte_layer_id_tut_nichts(self):
"""Prüft, dass eine leere gespeicherte Layer-ID das Projekt gar nicht erst anfragt."""
tab = _neuer_tab()
tab._atlas_layer_combo = MagicMock(spec=["setLayer"])
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value=""), \
patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
tab._restore_atlas_layer_selection()
tab._atlas_layer_combo.setLayer.assert_not_called()
mock_project.instance.assert_not_called()
class ThemeCollectionSignalsTests(unittest.TestCase):
def test_connect_verbindet_alle_signale(self):
"""Prüft, dass _connect_theme_collection_signals alle Signale der Theme-Collection verbindet."""
tab = _neuer_tab()
tab._theme_signal_connected = False
collection = _FakeThemeCollection()
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
mock_project.instance.return_value.mapThemeCollection.return_value = collection
tab._connect_theme_collection_signals()
self.assertEqual(collection.mapThemesChanged.connected, [tab._refresh_thema_combo_live])
self.assertTrue(tab._theme_signal_connected)
self.assertIs(tab._connected_theme_collection, collection)
def test_connect_ist_idempotent_wenn_schon_verbunden(self):
"""Prüft, dass _connect_theme_collection_signals bei bereits verbundenem Signal nichts erneut tut."""
tab = _neuer_tab()
tab._theme_signal_connected = True
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
tab._connect_theme_collection_signals()
mock_project.instance.assert_not_called()
def test_disconnect_trennt_alle_signale(self):
"""Prüft, dass _disconnect_theme_collection_signals alle verbundenen Theme-Signale trennt und zurücksetzt."""
tab = _neuer_tab()
collection = _FakeThemeCollection()
collection.mapThemesChanged.connect(tab._refresh_thema_combo_live)
collection.changed.connect(tab._refresh_thema_combo_live)
collection.themeChanged.connect(tab._refresh_thema_combo_live)
tab._connected_theme_collection = collection
tab._theme_signal_connected = True
tab._disconnect_theme_collection_signals()
self.assertEqual(collection.mapThemesChanged.connected, [])
self.assertIsNone(tab._connected_theme_collection)
self.assertFalse(tab._theme_signal_connected)
class RefreshThemaComboLiveTests(unittest.TestCase):
def test_erhaelt_vorherige_auswahl_wenn_noch_vorhanden(self):
"""Prüft, dass _refresh_thema_combo_live die vorherige Auswahl beibehält, wenn sie noch existiert."""
tab = _neuer_tab()
combo = _FakeCombo("Thema A")
tab._thema_combo = combo
tab._get_gespeicherte_themen = lambda: ["Thema A", "Thema B"]
tab._on_thema_changed = MagicMock()
tab._refresh_thema_combo_live()
self.assertEqual(combo.currentText(), "Thema A")
tab._on_thema_changed.assert_called_once_with("Thema A")
def test_faellt_auf_wie_kartenfenster_zurueck_wenn_auswahl_verschwunden(self):
"""Prüft, dass _refresh_thema_combo_live auf 'wie Kartenfenster' zurückfällt, wenn die Auswahl verschwunden ist."""
tab = _neuer_tab()
combo = _FakeCombo("Alte Auswahl")
tab._thema_combo = combo
tab._get_gespeicherte_themen = lambda: ["Thema B"]
tab._on_thema_changed = MagicMock()
tab._refresh_thema_combo_live()
self.assertEqual(combo.currentText(), THEMA_WIE_KARTENFENSTER)
tab._on_thema_changed.assert_called_once_with(THEMA_WIE_KARTENFENSTER)
class GetGespeicherteThemenTests(unittest.TestCase):
def test_dedupliziert_und_filtert_leere_eintraege(self):
"""Prüft, dass _get_gespeicherte_themen Duplikate und leere/None-Einträge entfernt."""
tab = _neuer_tab()
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
mock_project.instance.return_value.mapThemeCollection.return_value.mapThemes.return_value = [
"A", "A", "", "B", None,
]
ergebnis = tab._get_gespeicherte_themen()
self.assertEqual(ergebnis, ["A", "B"])
def test_fehler_liefert_leere_liste(self):
"""Prüft, dass ein Fehler beim Zugriff auf die Theme-Collection eine leere Liste liefert."""
tab = _neuer_tab()
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
mock_project.instance.side_effect = RuntimeError("kaputt")
ergebnis = tab._get_gespeicherte_themen()
self.assertEqual(ergebnis, [])
class GetCurrentCanvasScaleTests(unittest.TestCase):
def test_liefert_skalierung_als_float(self):
"""Prüft, dass _get_current_canvas_scale die Canvas-Skalierung als float liefert."""
tab = _neuer_tab()
fake_iface = MagicMock()
fake_iface.mapCanvas.return_value.scale.return_value = 5000
with patch("sn_basis.ui.tabs.print_tab.iface", fake_iface):
self.assertEqual(tab._get_current_canvas_scale(), 5000.0)
def test_ohne_canvas_liefert_none(self):
"""Prüft, dass _get_current_canvas_scale ohne verfügbares Kartenfenster None liefert."""
tab = _neuer_tab()
fake_iface = MagicMock()
fake_iface.mapCanvas.return_value = None
with patch("sn_basis.ui.tabs.print_tab.iface", fake_iface):
self.assertIsNone(tab._get_current_canvas_scale())
def test_skalierung_null_liefert_none(self):
"""Prüft, dass eine Skalierung von 0 als None statt als 0.0 gilt."""
tab = _neuer_tab()
fake_iface = MagicMock()
fake_iface.mapCanvas.return_value.scale.return_value = 0
with patch("sn_basis.ui.tabs.print_tab.iface", fake_iface):
self.assertIsNone(tab._get_current_canvas_scale())
class OnAtlasLayerChangedTests(unittest.TestCase):
def test_none_oder_leer_setzt_variable_leer(self):
"""Prüft, dass None oder ein leerer Wert die Abdeckungslayer-Variable leeren."""
tab = _neuer_tab()
with patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set:
tab._on_atlas_layer_changed(None)
tab._on_atlas_layer_changed("")
mock_set.assert_has_calls([
call("sn_abdeckungslayer", "", scope="project"),
call("sn_abdeckungslayer", "", scope="project"),
])
def test_layer_objekt_mit_id_methode_setzt_dessen_id(self):
"""Prüft, dass ein echtes Layer-Objekt (mit id()-Methode) dessen ID speichert."""
tab = _neuer_tab()
fake_layer = MagicMock()
fake_layer.id.return_value = "layer_42"
with patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set:
tab._on_atlas_layer_changed(fake_layer)
mock_set.assert_called_once_with("sn_abdeckungslayer", "layer_42", scope="project")
def test_string_fallback_sucht_layer_ueber_namen(self):
"""Prüft, dass ohne id()-Methode über den Layernamen im Projekt gesucht und dessen ID gespeichert wird."""
tab = _neuer_tab()
fake_layer = MagicMock(spec=["name"])
fake_layer.name.return_value = "Mein Layer"
with patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set, \
patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
mock_project.instance.return_value.mapLayers.return_value = {"layer_7": fake_layer}
tab._on_atlas_layer_changed("Mein Layer")
mock_set.assert_called_once_with("sn_abdeckungslayer", "layer_7", scope="project")
class OnAuswahlAsAtlasClickedTests(unittest.TestCase):
def test_erfolgreicher_layer_aktualisiert_atlas_ui(self):
"""Prüft, dass ein erfolgreich erzeugter Atlas-Layer den Atlas-Bereich öffnet und ins Dropdown übernimmt."""
tab = _neuer_tab()
fake_layer = object()
mock_logic = MagicMock()
mock_logic.create_atlas_layer_from_selection.return_value = (fake_layer, "layer_9")
tab.logic = mock_logic
tab.pruefmanager = object()
tab._atlas_button = MagicMock()
tab._atlas_content = MagicMock()
tab._atlas_layer_combo = MagicMock(spec=["setLayer"])
tab._toggle_atlas = MagicMock()
tab._on_auswahl_as_atlas_clicked()
tab._atlas_button.setChecked.assert_called_once_with(True)
tab._toggle_atlas.assert_called_once_with(True)
tab._atlas_layer_combo.setLayer.assert_called_once_with(fake_layer)
def test_ohne_erzeugten_layer_tut_nichts_weiter(self):
"""Prüft, dass ohne erzeugten Atlas-Layer keine UI-Aktualisierung stattfindet."""
tab = _neuer_tab()
mock_logic = MagicMock()
mock_logic.create_atlas_layer_from_selection.return_value = (None, None)
tab.logic = mock_logic
tab.pruefmanager = object()
tab._atlas_button = MagicMock()
tab._atlas_content = MagicMock()
tab._atlas_layer_combo = MagicMock()
tab._on_auswahl_as_atlas_clicked() # darf keine Ausnahme werfen
tab._atlas_button.setChecked.assert_not_called()
class PopulateAtlasLayerComboTests(unittest.TestCase):
def test_befuellt_combo_ohne_druckrechteck_und_leere_namen(self):
"""Prüft, dass _populate_atlas_layer_combo alle Layernamen außer 'Druckrechteck' und leeren Namen einträgt."""
tab = _neuer_tab()
combo = MagicMock(spec=["clear", "addItem", "blockSignals"])
tab._atlas_layer_combo = combo
layer_normal = MagicMock()
layer_normal.name.return_value = "Flurstuecke"
layer_druckrechteck = MagicMock()
layer_druckrechteck.name.return_value = "Druckrechteck"
layer_leer = MagicMock()
layer_leer.name.return_value = ""
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
mock_project.instance.return_value.mapLayers.return_value = {
"1": layer_normal, "2": layer_druckrechteck, "3": layer_leer,
}
tab._populate_atlas_layer_combo()
alle_aufrufe = [c.args[0] for c in combo.addItem.call_args_list]
self.assertIn("", alle_aufrufe)
self.assertIn("Flurstuecke", alle_aufrufe)
self.assertNotIn("Druckrechteck", alle_aufrufe)
self.assertEqual(alle_aufrufe.count(""), 1)
def test_ohne_atlas_layer_combo_tut_nichts(self):
"""Prüft, dass _populate_atlas_layer_combo ohne Atlas-Dropdown keine Ausnahme wirft."""
tab = _neuer_tab()
tab._atlas_layer_combo = None
tab._populate_atlas_layer_combo() # darf keine Ausnahme werfen
class OnAtlasErzeugenChangedTests(unittest.TestCase):
def test_aktiviert_speichert_variable_und_aktiviert_widgets(self):
"""Prüft, dass ein aktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button aktiviert."""
tab = _neuer_tab()
tab._atlas_layer_combo = MagicMock()
tab._btn_auswahl_as_atlas = MagicMock()
with patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set_variable:
tab._on_atlas_erzeugen_changed(1)
mock_set_variable.assert_called_once_with(ATLAS_ERZEUGEN_VAR, "1", scope="project")
tab._atlas_layer_combo.setEnabled.assert_called_once_with(True)
tab._btn_auswahl_as_atlas.setEnabled.assert_called_once_with(True)
def test_deaktiviert_speichert_variable_und_deaktiviert_widgets(self):
"""Prüft, dass ein deaktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button deaktiviert."""
tab = _neuer_tab()
tab._atlas_layer_combo = MagicMock()
tab._btn_auswahl_as_atlas = MagicMock()
with patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set_variable:
tab._on_atlas_erzeugen_changed(0)
mock_set_variable.assert_called_once_with(ATLAS_ERZEUGEN_VAR, "0", scope="project")
tab._atlas_layer_combo.setEnabled.assert_called_once_with(False)
tab._btn_auswahl_as_atlas.setEnabled.assert_called_once_with(False)
def test_ohne_widgets_wirft_keine_ausnahme(self):
"""Prüft, dass _on_atlas_erzeugen_changed ohne Atlas-Combo/-Button keine Ausnahme wirft."""
tab = _neuer_tab()
tab._atlas_layer_combo = None
tab._btn_auswahl_as_atlas = None
with patch("sn_basis.ui.tabs.print_tab.set_variable"):
tab._on_atlas_erzeugen_changed(1) # darf keine Ausnahme werfen
if __name__ == "__main__":
unittest.main()
+16 -1
View File
@@ -1,6 +1,7 @@
"""Tests für sn_basis/modules/pruef_ergebnis.py::ist_verfahrens_db_aktion/erlaubte_persistierung (Issue #65)."""
"""Tests für sn_basis/modules/pruef_ergebnis.py::ist_verfahrens_db_aktion/erlaubte_persistierung/dateipfad (Issue #65/#67)."""
import unittest
from pathlib import Path
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
@@ -43,5 +44,19 @@ class ErlaubtePersistierungTests(unittest.TestCase):
self.assertFalse(ergebnis.erlaubte_persistierung)
class DateipfadTests(unittest.TestCase):
def test_liefert_path_wenn_kontext_ein_path_ist(self):
"""Prüft, dass dateipfad den Kontext liefert, wenn dieser bereits ein Path-Objekt ist."""
ergebnis = pruef_ergebnis(ok=True, kontext=Path("C:/tmp/verfahren.gpkg"))
self.assertEqual(ergebnis.dateipfad, Path("C:/tmp/verfahren.gpkg"))
def test_liefert_none_wenn_kontext_kein_path_ist(self):
"""Prüft, dass dateipfad None liefert, wenn der Kontext kein Path-Objekt ist (z. B. String oder Layer)."""
self.assertIsNone(pruef_ergebnis(ok=True, kontext="C:/tmp/verfahren.gpkg").dateipfad)
self.assertIsNone(pruef_ergebnis(ok=True, kontext=None).dateipfad)
self.assertIsNone(pruef_ergebnis(ok=True, kontext=object()).dateipfad)
if __name__ == "__main__":
unittest.main()
+21 -1
View File
@@ -1,4 +1,4 @@
"""Tests für sn_basis/functions/qgiscore_wrapper.py::polygon_zu_linie/layer_exists_in_gpkg (Issue #65)."""
"""Tests für sn_basis/functions/qgiscore_wrapper.py::polygon_zu_linie/layer_exists_in_gpkg/create_buffer_layer (Issue #65/#67)."""
import sqlite3
import tempfile
@@ -164,5 +164,25 @@ class LayerExistsInGpkgTests(unittest.TestCase):
self.assertFalse(qc.layer_exists_in_gpkg(pfad, "Flurstuecke"))
class CreateBufferLayerTests(unittest.TestCase):
"""Deckt nur die Wächter ab: die eigentliche Pufferbildung importiert `qgis.core.QgsFeature`
direkt und ist im Mock-Modus nicht sinnvoll prüfbar (siehe Docstring der Funktion)."""
def test_ohne_qgis_liefert_none(self):
"""Prüft, dass create_buffer_layer ohne verfügbares QGIS None liefert."""
with patch.object(qc, "QGIS_AVAILABLE", False):
self.assertIsNone(qc.create_buffer_layer(source_layer=object(), distance_m=10.0))
def test_fehlender_source_layer_liefert_none(self):
"""Prüft, dass ein fehlender Quelllayer (None) None liefert, auch wenn QGIS verfügbar wäre."""
with patch.object(qc, "QGIS_AVAILABLE", True):
self.assertIsNone(qc.create_buffer_layer(source_layer=None, distance_m=10.0))
def test_source_layer_ohne_getfeatures_liefert_none(self):
"""Prüft, dass ein Quelllayer ohne getFeatures()-Methode None liefert."""
with patch.object(qc, "QGIS_AVAILABLE", True):
self.assertIsNone(qc.create_buffer_layer(source_layer=object(), distance_m=10.0))
if __name__ == "__main__":
unittest.main()
+70 -1
View File
@@ -1,4 +1,4 @@
"""Tests für sn_basis/functions/qgisui_wrapper.py::add_layer_to_project (Issue #65)."""
"""Tests für sn_basis/functions/qgisui_wrapper.py::add_layer_to_project/find_dock_widgets/add_menu/remove_menu (Issue #65/#67)."""
import unittest
from unittest.mock import MagicMock, patch
@@ -34,5 +34,74 @@ class AddLayerToProjectTests(unittest.TestCase):
self.assertFalse(qui.add_layer_to_project(object()))
class FindDockWidgetsTests(unittest.TestCase):
def test_ohne_main_window_liefert_leere_liste(self):
"""Prüft, dass find_dock_widgets ohne verfügbares Hauptfenster eine leere Liste liefert."""
with patch.object(qui, "get_main_window", return_value=None):
self.assertEqual(qui.find_dock_widgets(), [])
def test_liefert_dock_widgets_des_main_window(self):
"""Prüft, dass find_dock_widgets die vom Hauptfenster gefundenen Dock-Widgets durchreicht."""
fake_docks = [object(), object()]
fake_window = MagicMock()
fake_window.findChildren.return_value = fake_docks
with patch.object(qui, "get_main_window", return_value=fake_window):
self.assertEqual(qui.find_dock_widgets(), fake_docks)
def test_fehler_beim_suchen_liefert_leere_liste(self):
"""Prüft, dass ein Fehler beim Suchen der Dock-Widgets abgefangen wird und eine leere Liste liefert."""
fake_window = MagicMock()
fake_window.findChildren.side_effect = RuntimeError("kaputt")
with patch.object(qui, "get_main_window", return_value=fake_window):
self.assertEqual(qui.find_dock_widgets(), [])
class _FakeMenu:
def __init__(self) -> None:
self.menuAction = lambda: "MENU_ACTION"
class AddRemoveMenuTests(unittest.TestCase):
def test_add_menu_ohne_main_window_tut_nichts(self):
"""Prüft, dass add_menu ohne Hauptfenster keine Ausnahme wirft."""
fake_iface = MagicMock()
fake_iface.mainWindow.return_value = None
with patch.object(qui, "iface", fake_iface):
qui.add_menu(_FakeMenu()) # darf keine Ausnahme werfen
def test_add_menu_mit_echtem_qt_menu_fuegt_es_der_menubar_hinzu(self):
"""Prüft, dass ein echtes Qt-Menü (mit menuAction) der Menüleiste hinzugefügt wird."""
menu = _FakeMenu()
fake_iface = MagicMock()
with patch.object(qui, "iface", fake_iface):
qui.add_menu(menu)
fake_iface.mainWindow.return_value.menuBar.return_value.addMenu.assert_called_once_with(menu)
def test_add_menu_ohne_menuaction_wird_ignoriert(self):
"""Prüft, dass ein Objekt ohne menuAction nicht an die Menüleiste übergeben wird."""
fake_iface = MagicMock()
with patch.object(qui, "iface", fake_iface):
qui.add_menu(object())
fake_iface.mainWindow.return_value.menuBar.return_value.addMenu.assert_not_called()
def test_remove_menu_entfernt_ueber_menuaction(self):
"""Prüft, dass remove_menu das Menü über dessen menuAction() aus der Menüleiste entfernt."""
menu = _FakeMenu()
fake_iface = MagicMock()
with patch.object(qui, "iface", fake_iface):
qui.remove_menu(menu)
fake_iface.mainWindow.return_value.menuBar.return_value.removeAction.assert_called_once_with("MENU_ACTION")
def test_remove_menu_ohne_main_window_tut_nichts(self):
"""Prüft, dass remove_menu ohne Hauptfenster keine Ausnahme wirft."""
fake_iface = MagicMock()
fake_iface.mainWindow.return_value = None
with patch.object(qui, "iface", fake_iface):
qui.remove_menu(_FakeMenu()) # darf keine Ausnahme werfen
if __name__ == "__main__":
unittest.main()
+22 -1
View File
@@ -1,7 +1,8 @@
"""Tests für sn_basis/ui/tabs/settings_tab.py::save_data/load_data (Issue #65)."""
"""Tests für sn_basis/ui/tabs/settings_tab.py::save_data/load_data/_build_input (Issue #65/#67)."""
import unittest
from sn_basis.functions.landkreise import LANDKREISE
from sn_basis.functions.qt_wrapper import QComboBox, QLineEdit
from sn_basis.ui.tabs.settings_tab import SettingsTab
@@ -113,5 +114,25 @@ class LoadDataTests(unittest.TestCase):
self.assertEqual(combo.currentText(), "")
class BuildInputTests(unittest.TestCase):
def test_landkreis_feld_liefert_combobox_mit_allen_landkreisen(self):
"""Prüft, dass _build_input für das Landkreis-Feld eine ComboBox mit Platzhalter und allen Landkreisen liefert."""
tab = _neuer_tab(_FakeLogic())
widget = tab._build_input("landkreis_user")
self.assertIsInstance(widget, QComboBox)
self.assertEqual(widget.count(), 1 + len(LANDKREISE))
def test_anderes_feld_liefert_lineedit(self):
"""Prüft, dass _build_input für ein normales Feld ein leeres Textfeld liefert."""
tab = _neuer_tab(_FakeLogic())
widget = tab._build_input("amt")
self.assertIsInstance(widget, QLineEdit)
self.assertEqual(widget.text(), "")
if __name__ == "__main__":
unittest.main()
+1
View File
@@ -113,6 +113,7 @@ class PrintTab(QWidget):
self.logic.set_formfaktor(self._endlosrolle_cb.isChecked())
def _build_ui(self) -> None:
"""von Test abgesehen, weil vernachlässigbar; HD 18.09.2026"""
main_layout = QVBoxLayout()
main_layout.setSpacing(4)
main_layout.setContentsMargins(4, 4, 4, 4)