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:
@@ -132,6 +132,7 @@ als eigene Filter-Chips „Nie geprüft" / „Veraltet geprüft".
|
|||||||
```bash
|
```bash
|
||||||
python scripts/test_audit.py status sn_verfahrensgebiet # Prüfstatus je Testfall anzeigen
|
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 --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
|
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
|
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.
|
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
|
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
|
zutreffen. Ob überhaupt genug getestet wird, zeigen die Testkonzept-Seiten — das ist der nächste, gesonderte
|
||||||
Schritt, kein Ersatz für das Audit.
|
Schritt, kein Ersatz für das Audit.
|
||||||
|
|||||||
+14
-2
@@ -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
|
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).
|
(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):
|
Aufrufe (aus dem Workspace-Root):
|
||||||
python scripts/test_audit.py status # alle Pakete
|
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_verfahrensgebiet # nur ein Paket
|
||||||
python scripts/test_audit.py status sn_basis --nur ungeprueft # Filter auf Prüfstatus
|
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 --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 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.*" --trocken # nur anzeigen, nichts schreiben
|
||||||
python scripts/test_audit.py markieren sn_basis TestX.test_x --datum 2026-08-01 # Nacherfassung
|
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
|
continue
|
||||||
if gesucht_status and fall.pruefstatus != gesucht_status:
|
if gesucht_status and fall.pruefstatus != gesucht_status:
|
||||||
continue
|
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
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@@ -148,7 +159,7 @@ def befehl_markieren(args: argparse.Namespace) -> int:
|
|||||||
pruefungen, _ = tk.lies_pruefungen(args.pruefungsdatei)
|
pruefungen, _ = tk.lies_pruefungen(args.pruefungsdatei)
|
||||||
tk.setze_pruefstatus(katalog, pruefungen)
|
tk.setze_pruefstatus(katalog, pruefungen)
|
||||||
for datei, fall in treffer:
|
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:
|
if args.trocken:
|
||||||
print(f"Trockenlauf – nichts geschrieben. {len(treffer)} Treffer.")
|
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("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("--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("--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("--baseline-datei", type=Path, default=tk.BASELINE_DEFAULT)
|
||||||
p_status.add_argument("--pruefungsdatei", type=Path, default=tk.PRUEFUNGEN_DEFAULT)
|
p_status.add_argument("--pruefungsdatei", type=Path, default=tk.PRUEFUNGEN_DEFAULT)
|
||||||
p_status.set_defaults(func=befehl_status)
|
p_status.set_defaults(func=befehl_status)
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ class Testfall:
|
|||||||
quellhash: str = ""
|
quellhash: str = ""
|
||||||
pruefstatus: str = "nie geprüft"
|
pruefstatus: str = "nie geprüft"
|
||||||
pruefdatum: str = ""
|
pruefdatum: str = ""
|
||||||
|
zeile: int = 0
|
||||||
|
quelltext: str = ""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bezeichnung(self) -> str:
|
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 ""),
|
grund=grund or (klassen_grund if klassen_skip else ""),
|
||||||
erwarteter_fehlschlag=erwartet or klassen_erwartet,
|
erwarteter_fehlschlag=erwartet or klassen_erwartet,
|
||||||
quellhash=_funktionshash(zeilen, glied),
|
quellhash=_funktionshash(zeilen, glied),
|
||||||
|
zeile=glied.lineno,
|
||||||
|
quelltext=_funktionsquelle(zeilen, glied),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if gruppe.faelle:
|
if gruppe.faelle:
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ class WorkspaceGestuetzteTests(unittest.TestCase):
|
|||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
args = argparse.Namespace(
|
args = argparse.Namespace(
|
||||||
pakete=[], muster=None, nur=None,
|
pakete=[], muster=None, nur=None, code=False,
|
||||||
baseline_datei=self.root / "keine_baseline.json",
|
baseline_datei=self.root / "keine_baseline.json",
|
||||||
pruefungsdatei=self.pruefungsdatei,
|
pruefungsdatei=self.pruefungsdatei,
|
||||||
)
|
)
|
||||||
@@ -184,6 +184,20 @@ class WorkspaceGestuetzteTests(unittest.TestCase):
|
|||||||
self.assertEqual(code, 0)
|
self.assertEqual(code, 0)
|
||||||
self.assertIn("test_eins", ausgabe)
|
self.assertIn("test_eins", ausgabe)
|
||||||
self.assertIn("nie geprüft", 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):
|
def test_markieren_ohne_treffer_gibt_exitcode_1(self):
|
||||||
"""Prüft, dass ein Muster ohne Treffer Exit-Code 1 liefert und keine Datei schreibt."""
|
"""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())
|
self.assertTrue(self.pruefungsdatei.exists())
|
||||||
|
|
||||||
status_args = argparse.Namespace(
|
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",
|
baseline_datei=self.root / "keine_baseline.json",
|
||||||
pruefungsdatei=self.pruefungsdatei,
|
pruefungsdatei=self.pruefungsdatei,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user