From ec400ae59e348b175066cb7922e01317baf129dc Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 16 Sep 2026 12:45:12 +0200 Subject: [PATCH] =?UTF-8?q?Test-Audit:=20Datei:Zeile=20und=20Quelltext-Aus?= =?UTF-8?q?gabe=20f=C3=BCr=20bequemes=20Ansehen/Editieren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit status zeigt je Treffer ":" (in Editor-Terminals anklickbar) und optional per --code den Testquelltext im Terminal. Der Prüfstand selbst bleibt ohne Quelltext, um Diff-Lärm bei jeder Codeänderung zu vermeiden. Co-Authored-By: Claude Sonnet 5 --- docs/leitfaden/06_tests.md | 6 ++++++ scripts/test_audit.py | 16 ++++++++++++++-- scripts/testkatalog.py | 4 ++++ scripts/tests/test_test_audit.py | 18 ++++++++++++++++-- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/docs/leitfaden/06_tests.md b/docs/leitfaden/06_tests.md index 938eae9..c61fea6 100644 --- a/docs/leitfaden/06_tests.md +++ b/docs/leitfaden/06_tests.md @@ -132,6 +132,7 @@ als eigene Filter-Chips „Nie geprüft" / „Veraltet geprüft". ```bash python scripts/test_audit.py status sn_verfahrensgebiet # Prüfstatus je Testfall anzeigen python scripts/test_audit.py status --nur ungeprueft # nur ungeprüfte Testfälle +python scripts/test_audit.py status sn_basis --muster TestX --code # mit Quelltext im Terminal python scripts/test_audit.py markieren sn_basis TestX.test_y # als heute geprüft markieren ``` @@ -139,6 +140,11 @@ python scripts/test_audit.py markieren sn_basis TestX.test_y # als heute gep Treffer, ohne zu schreiben — vor einem Muster mit vielen Treffern empfohlen. Das Skript importiert die Katalog-Logik aus `scripts/testkatalog.py`, statt sie zu duplizieren. +`status` zeigt zu jedem Treffer „:" — in den meisten Editor-Terminals anklickbar, um den Test +direkt zu öffnen und zu bearbeiten. Der Prüfstand selbst zeigt bewusst **keinen** Quelltext: er ist eine +versionierte, generierte Datei, und Quelltext darin würde bei jeder Codeänderung riesige, wenig aussagekräftige +Diffs erzeugen. Zum Lesen ohne Editor dient `--code`, zum Bearbeiten der übliche Weg über Konsole/Editor. + Abgrenzung zu Abschnitt 8 (Testkonzepte): Das Audit bestätigt, dass **vorhandene** Tests noch fachlich zutreffen. Ob überhaupt genug getestet wird, zeigen die Testkonzept-Seiten — das ist der nächste, gesonderte Schritt, kein Ersatz für das Audit. diff --git a/scripts/test_audit.py b/scripts/test_audit.py index 10b96c9..fda4d5c 100644 --- a/scripts/test_audit.py +++ b/scripts/test_audit.py @@ -11,11 +11,17 @@ sichtbar, welche Tests nie geprüft wurden oder sich seit der letzten Prüfung g Reine Standardbibliothek, kein Import von sn_basis/QGIS nötig, da nur mit ast geparst wird (genau wie testkatalog.py, dessen Funktionen hier wiederverwendet werden). +`status` zeigt je Treffer ":" – in den meisten Terminals/Editoren (z. B. VS Code) anklickbar, +um den Test direkt zum Bearbeiten zu öffnen. `--code` gibt zusätzlich den Testquelltext im Terminal aus, zum +schnellen fachlichen Lesen ohne Editor. Bearbeitet wird die Testdatei ganz normal über die Konsole/den Editor – +dieses Skript schreibt nur `docs/status/testpruefungen.json`, nie Testdateien. + Aufrufe (aus dem Workspace-Root): python scripts/test_audit.py status # alle Pakete python scripts/test_audit.py status sn_verfahrensgebiet # nur ein Paket python scripts/test_audit.py status sn_basis --nur ungeprueft # Filter auf Prüfstatus python scripts/test_audit.py status --muster grenzpunkt # Filter auf Bezeichner + python scripts/test_audit.py status sn_basis --muster Test_x --code # mit Quelltext (zum Lesen, nicht editierbar) python scripts/test_audit.py markieren sn_basis TestHallo.test_x # als heute geprüft markieren python scripts/test_audit.py markieren sn_basis "TestX.*" --trocken # nur anzeigen, nichts schreiben python scripts/test_audit.py markieren sn_basis TestX.test_x --datum 2026-08-01 # Nacherfassung @@ -99,7 +105,12 @@ def befehl_status(args: argparse.Namespace) -> int: continue if gesucht_status and fall.pruefstatus != gesucht_status: continue - print(f" {datei.anzeigepfad}::{fall.bezeichnung} -> {tk.pruef_text(fall)}") + print( + f" {datei.anzeigepfad}:{fall.zeile} {fall.bezeichnung} -> {tk.pruef_text(fall)}" + ) + if args.code: + for zeile in fall.quelltext.splitlines(): + print(f" {zeile}") return 0 @@ -148,7 +159,7 @@ def befehl_markieren(args: argparse.Namespace) -> int: pruefungen, _ = tk.lies_pruefungen(args.pruefungsdatei) tk.setze_pruefstatus(katalog, pruefungen) for datei, fall in treffer: - print(f"{datei.anzeigepfad}::{fall.bezeichnung} (vorher: {tk.pruef_text(fall)})") + print(f"{datei.anzeigepfad}:{fall.zeile} {fall.bezeichnung} (vorher: {tk.pruef_text(fall)})") if args.trocken: print(f"Trockenlauf – nichts geschrieben. {len(treffer)} Treffer.") @@ -170,6 +181,7 @@ def main() -> int: p_status.add_argument("pakete", nargs="*", help="Paketnamen (Standard: alle mit tests=true)") p_status.add_argument("--muster", help="nur Testfälle, deren Bezeichner diesen Text enthält (Teilstring)") p_status.add_argument("--nur", choices=sorted(NUR_ZU_PRUEFSTATUS), help="nur diesen Prüfstatus anzeigen") + p_status.add_argument("--code", action="store_true", help="Quelltext jeder passenden Testmethode mit ausgeben") p_status.add_argument("--baseline-datei", type=Path, default=tk.BASELINE_DEFAULT) p_status.add_argument("--pruefungsdatei", type=Path, default=tk.PRUEFUNGEN_DEFAULT) p_status.set_defaults(func=befehl_status) diff --git a/scripts/testkatalog.py b/scripts/testkatalog.py index 03b1537..588c1a7 100644 --- a/scripts/testkatalog.py +++ b/scripts/testkatalog.py @@ -96,6 +96,8 @@ class Testfall: quellhash: str = "" pruefstatus: str = "nie geprüft" pruefdatum: str = "" + zeile: int = 0 + quelltext: str = "" @property def bezeichnung(self) -> str: @@ -262,6 +264,8 @@ def lies_testdatei(pfad: Path, paket: str) -> Testdatei: grund=grund or (klassen_grund if klassen_skip else ""), erwarteter_fehlschlag=erwartet or klassen_erwartet, quellhash=_funktionshash(zeilen, glied), + zeile=glied.lineno, + quelltext=_funktionsquelle(zeilen, glied), ) ) if gruppe.faelle: diff --git a/scripts/tests/test_test_audit.py b/scripts/tests/test_test_audit.py index a15f669..1b9c656 100644 --- a/scripts/tests/test_test_audit.py +++ b/scripts/tests/test_test_audit.py @@ -176,7 +176,7 @@ class WorkspaceGestuetzteTests(unittest.TestCase): import argparse args = argparse.Namespace( - pakete=[], muster=None, nur=None, + pakete=[], muster=None, nur=None, code=False, baseline_datei=self.root / "keine_baseline.json", pruefungsdatei=self.pruefungsdatei, ) @@ -184,6 +184,20 @@ class WorkspaceGestuetzteTests(unittest.TestCase): self.assertEqual(code, 0) self.assertIn("test_eins", ausgabe) self.assertIn("nie geprüft", ausgabe) + self.assertRegex(ausgabe, r"test_beispiel\.py:\d+") + + def test_status_befehl_mit_code_gibt_quelltext_aus(self): + """Prüft, dass --code den Testquelltext der passenden Methode mit ausgibt.""" + import argparse + + args = argparse.Namespace( + pakete=[], muster="test_eins", nur=None, code=True, + baseline_datei=self.root / "keine_baseline.json", + pruefungsdatei=self.pruefungsdatei, + ) + _, ausgabe = self._stdout(lambda: test_audit.befehl_status(args)) + self.assertIn("def test_eins(self):", ausgabe) + self.assertIn("assertEqual(1, 1)", ausgabe) def test_markieren_ohne_treffer_gibt_exitcode_1(self): """Prüft, dass ein Muster ohne Treffer Exit-Code 1 liefert und keine Datei schreibt.""" @@ -210,7 +224,7 @@ class WorkspaceGestuetzteTests(unittest.TestCase): self.assertTrue(self.pruefungsdatei.exists()) status_args = argparse.Namespace( - pakete=[], muster="test_eins", nur=None, + pakete=[], muster="test_eins", nur=None, code=False, baseline_datei=self.root / "keine_baseline.json", pruefungsdatei=self.pruefungsdatei, )