Test-Audit: Datei:Zeile und Quelltext-Ausgabe für bequemes Ansehen/Editieren

status zeigt je Treffer "<Datei>:<Zeile>" (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 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 12:45:12 +02:00
co-authored by Claude Sonnet 5
parent 5dfec4f934
commit ec400ae59e
4 changed files with 40 additions and 4 deletions
+6
View File
@@ -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 „<Datei>:<Zeile>" — 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.
+14 -2
View File
@@ -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 "<datei>:<zeile>" – 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)
+4
View File
@@ -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:
+16 -2
View File
@@ -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,
)