197 lines
11 KiB
Markdown
197 lines
11 KiB
Markdown
# Tests: was geprüft wird, wie man es liest, was Pflicht ist
|
||||
|
|
|
|||
|
|
Stand 04.09.2026. Für alle, die an den LNO-Plugins mitarbeiten — auch für Fachleute ohne Programmiererfahrung.
|
|||
|
|
Der Abschnitt 7 ist ausdrücklich für Sie geschrieben: dort geht es darum, zu **beurteilen**, ob eine Testsuite
|
|||
|
|
fachlich das Richtige prüft. Dafür muss man keinen Code lesen können.
|
|||
|
|
|
|||
|
|
## 1. Warum das hier ernster genommen wird als sonst
|
|||
|
|
|
|||
|
|
Ein großer Teil des Codes entsteht mit KI-Agenten. Ein Agent kann überzeugend erklären, dass etwas funktioniert,
|
|||
|
|
ohne dass es funktioniert. Er kann auch einen roten Test „reparieren", indem er die Erwartung an das falsche
|
|||
|
|
Verhalten anpasst. Tests sind der einzige schnelle, nachprüfbare Nachweis, den ein Review hat.
|
|||
|
|
|
|||
|
|
Deshalb gilt: **Neue Funktionalität ohne Test wird nicht gemergt.** Und: Tests werden nicht gelöscht, nicht
|
|||
|
|
übersprungen und nicht in ihrer Erwartung angepasst, um grün zu werden — außer das Verhalten wurde bewusst
|
|||
|
|
geändert, und der Pull Request begründet es.
|
|||
|
|
|
|||
|
|
## 2. Die drei Ebenen
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TB
|
|||
|
|
M["<b>Manueller Test in QGIS</b><br/>wenige · Minuten bis Stunden · durch Menschen<br/><i>Bedienbarkeit, Optik, Zusammenspiel mit echten Daten</i>"]
|
|||
|
|
Q["<b>QGIS-Tests</b> — geplant, siehe konzept/07<br/>einige Dutzend · Minuten · automatisch<br/><i>Layer, WFS, Drucklayouts, Docks gegen echtes QGIS</i>"]
|
|||
|
|
K["<b>Mock-Tests</b> — heute 332 Stück<br/>viele · unter einer Sekunde · automatisch, ohne QGIS<br/><i>Logik, Parsing, Prüfregeln, Fehlerbehandlung</i>"]
|
|||
|
|
|
|||
|
|
M --- Q --- K
|
|||
|
|
|
|||
|
|
style M fill:#ffe9e9,stroke:#d94b2b
|
|||
|
|
style Q fill:#e6f2ff,stroke:#2b7dd9
|
|||
|
|
style K fill:#e8f6ec,stroke:#2f9e44
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Je weiter unten, desto schneller, billiger und häufiger. Ein Fehler soll möglichst weit unten auffallen — dort
|
|||
|
|
ist er in Sekunden zu finden statt in einer halben Stunde Klickerei.
|
|||
|
|
|
|||
|
|
**Mock-Tests** laufen ohne QGIS, weil aller Zugriff auf QGIS und Qt über die Wrapper in `sn_basis.functions`
|
|||
|
|
läuft. Fehlt QGIS, liefern die Wrapper Ersatzobjekte. Das ist der Grund für die Wrapper-Regel in `AGENTS.md` —
|
|||
|
|
sie ist nicht Selbstzweck, sondern das, was diese Ebene überhaupt möglich macht.
|
|||
|
|
|
|||
|
|
**QGIS-Tests** gibt es heute noch nicht automatisiert. Der Stufenplan steht in
|
|||
|
|
[`docs/konzept/07_qgis_tests_plan.md`](../konzept/07_qgis_tests_plan.md).
|
|||
|
|
|
|||
|
|
**Manuelle Tests** bleiben unersetzbar für alles, was ein Mensch sehen muss: Sieht der Plan richtig aus? Ist der
|
|||
|
|
Dialog bedienbar? Stimmen die Beschriftungen fachlich?
|
|||
|
|
|
|||
|
|
## 3. Was gehört auf welche Ebene
|
|||
|
|
|
|||
|
|
| Was geändert wurde | Mock-Test | QGIS-Test | Manuell |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| Rechenregel, Prüflogik, Schlüsselableitung | **Pflicht** | – | – |
|
|||
|
|
| Einlesen einer Datei (Shape, Excel, CSV, ZIP) | **Pflicht** | wenn Geometrie entsteht | – |
|
|||
|
|
| Fehlerbehandlung, Sonderfälle, leere Eingaben | **Pflicht** | – | – |
|
|||
|
|
| Aufruf eines Webdienstes (WFS, VLN, ALKIS) | **Pflicht** (Antwort nachgestellt) | empfohlen | bei neuem Dienst |
|
|||
|
|
| Layer anlegen, stylen, in den Baum hängen | soweit möglich | **Pflicht** | – |
|
|||
|
|
| Drucklayout, Planausgabe | Logik ja | **Pflicht** | **Pflicht** (Aussehen) |
|
|||
|
|
| Dialog, Dock, Tab, Signalverdrahtung | Logik dahinter | empfohlen | **Pflicht** |
|
|||
|
|
| Menü, Toolbar, Plugin-Ladevorgang | – | empfohlen | **Pflicht** |
|
|||
|
|
| Nur Dokumentation | – | – | – |
|
|||
|
|
|
|||
|
|
Faustregel: **Alles, was ohne sichtbare Oberfläche entscheidbar ist, gehört in einen Mock-Test.** Wenn sich etwas
|
|||
|
|
schlecht testen lässt, liegt das meistens daran, dass Fachlogik und Oberfläche vermischt sind — dann ist die
|
|||
|
|
Trennung die eigentliche Aufgabe, nicht der Test.
|
|||
|
|
|
|||
|
|
## 4. Die Pflicht im Detail
|
|||
|
|
|
|||
|
|
**Bei neuer Funktionalität:** mindestens ein Test je neuem beobachtbarem Verhalten. Nicht je Funktion — je
|
|||
|
|
*Verhalten*. Eine Funktion, die drei Fälle unterscheidet, braucht drei Tests.
|
|||
|
|
|
|||
|
|
**Bei Änderungen an bestehender Logik:**
|
|||
|
|
1. Vorher `python scripts/run_tests.py <paket> --baseline` laufen lassen und die Zahl merken.
|
|||
|
|
2. Ändern.
|
|||
|
|
3. Erneut laufen lassen. Keine Zeile `NEU ROT`.
|
|||
|
|
4. Die Tests, die das geänderte Verhalten abdecken, erweitern oder anpassen — mit Begründung im Pull Request.
|
|||
|
|
|
|||
|
|
**Bei einer Fehlerbehebung:** zuerst einen Test schreiben, der den Fehler zeigt (also rot ist), dann beheben.
|
|||
|
|
Sonst weiß niemand, ob der Fehler wirklich weg ist oder nur nicht mehr auffällt.
|
|||
|
|
|
|||
|
|
## 5. Wie ein guter Test aussieht
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def test_fbschl_mit_punkt_wird_erkannt(self):
|
|||
|
|
"""Prüft, dass der Shape-Import auch das Feld 'FBSchl.' mit Punkt akzeptiert.
|
|||
|
|
|
|||
|
|
Manche DAVID-Exporte liefern den Feldnamen mit Punkt, andere ohne.
|
|||
|
|
Beide müssen zum selben Gebietstyp führen.
|
|||
|
|
"""
|
|||
|
|
layer = self._layer_mit_feldern({"FBSchl.": "901/10110F"})
|
|||
|
|
self.assertEqual(_ermittle_gebietstyp_aus_fbschl(layer), "Verfahrensgebiet")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Drei Anforderungen, alle drei verbindlich:
|
|||
|
|
|
|||
|
|
1. **Der Name sagt, was geprüft wird**, auf Deutsch, aus fachlicher Sicht:
|
|||
|
|
`test_fbschl_mit_punkt_wird_erkannt` — nicht `test_case_3` oder `test_import`.
|
|||
|
|
2. **Ein deutscher Docstring, erste Zeile in der Form `Prüft, dass …`.** Diese Zeile landet im Testkatalog
|
|||
|
|
(Abschnitt 6) und ist für viele Leser der einzige Kontakt mit dem Test. Wenn nötig, danach ein Absatz mit dem
|
|||
|
|
fachlichen Hintergrund.
|
|||
|
|
3. **Ein Verhalten je Test.** Wenn im Namen ein „und" steht, sind es zwei Tests.
|
|||
|
|
|
|||
|
|
Weiter gilt: `unittest`, Datei `tests/test_<modul>.py`, kein Netzwerkzugriff, keine echten Verfahrensdaten, keine
|
|||
|
|
Abhängigkeit von der Reihenfolge der Tests. Muster liefern die vorhandenen Tests des jeweiligen Pakets.
|
|||
|
|
|
|||
|
|
## 6. Der Testkatalog
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python scripts/testkatalog.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
erzeugt [`docs/tests/testkatalog.md`](../tests/testkatalog.md): jeder Testfall aller Pakete mit seiner
|
|||
|
|
Docstring-Zeile und seinem Status. Die Datei wird aus dem Code erzeugt und ist damit immer aktuell — sie wird
|
|||
|
|
**nicht von Hand bearbeitet**.
|
|||
|
|
|
|||
|
|
Wozu das gut ist: Man sieht auf zwei Seiten, was die Suite abdeckt, ohne eine einzige Testdatei zu öffnen. Der
|
|||
|
|
Abschnitt „Lücken" am Ende listet alle Tests ohne Docstring — das ist die Arbeitsliste, um den Katalog
|
|||
|
|
vollständig lesbar zu machen.
|
|||
|
|
|
|||
|
|
Nach dem Hinzufügen von Tests den Katalog neu erzeugen und mit committen. `python scripts/testkatalog.py --pruefen`
|
|||
|
|
meldet, wenn er veraltet ist.
|
|||
|
|
|
|||
|
|
## 7. Testkonzepte: prüfen, ob die Tests fachlich sinnvoll sind
|
|||
|
|
|
|||
|
|
Der Katalog beantwortet „was wird getestet". Er beantwortet **nicht** „ist das genug". Dafür gibt es je
|
|||
|
|
wichtigem Fachablauf eine eigene Seite in [`docs/tests/`](../tests/), nach der Vorlage
|
|||
|
|
[`docs/vorlagen/testkonzept.md`](../vorlagen/testkonzept.md).
|
|||
|
|
|
|||
|
|
Jede dieser Seiten hat denselben Aufbau:
|
|||
|
|
|
|||
|
|
1. **Ein Mermaid-Diagramm des fachlichen Ablaufs** — so, wie ein Sachbearbeiter ihn beschreiben würde.
|
|||
|
|
Keine Klassennamen, keine Dateipfade.
|
|||
|
|
2. **Eine Tabelle: Schritt → welcher Test deckt ihn ab → was bewusst nicht getestet wird, und warum.**
|
|||
|
|
3. **Ein Abschnitt „Bekannte Lücken"** mit Issue-Nummer, wo etwas fehlt.
|
|||
|
|
|
|||
|
|
So können Sie als Fachperson prüfen, ohne Code zu lesen:
|
|||
|
|
|
|||
|
|
- Kommt jeder Schritt Ihres Ablaufs in der Tabelle vor? Fehlt einer, fehlt wahrscheinlich auch der Test.
|
|||
|
|
- Sind die **Sonderfälle** dabei, die in der Praxis wirklich vorkommen — der leere Export, die doppelte
|
|||
|
|
Gemarkung, der Datensatz aus dem Nachbarlandkreis?
|
|||
|
|
- Steht bei „bewusst nicht getestet" eine Begründung, die Sie nachvollziehen können? „Zu aufwendig" ist keine.
|
|||
|
|
- Beschreibt der Test das *Ergebnis* oder nur, dass etwas *durchläuft*? „Prüft, dass der Import nicht abstürzt"
|
|||
|
|
ist deutlich weniger wert als „Prüft, dass aus 901/10110F der Gebietstyp Verfahrensgebiet wird".
|
|||
|
|
|
|||
|
|
Ein Beispiel liegt in [`docs/tests/verfahrensgebiet-shape-import.md`](../tests/verfahrensgebiet-shape-import.md).
|
|||
|
|
|
|||
|
|
Wenn Sie eine Lücke finden: Issue aufmachen (`python scripts/gitea.py entwurf issue Verfahrensgebiet` legt einen
|
|||
|
|
Entwurf an). Eine gefundene Testlücke ist ein vollwertiges Ticket.
|
|||
|
|
|
|||
|
|
## 8. Die Baseline
|
|||
|
|
|
|||
|
|
Nicht alle Tests sind grün. Der Ist-Stand steht in [`docs/status/testbaseline.json`](../status/testbaseline.json)
|
|||
|
|
und wirkt als Sperrklinke: **bekannte rote Tests dürfen bleiben, neue nicht dazukommen.**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python scripts/run_tests.py sn_verfahrensgebiet --baseline
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `NEU ROT` — nicht erlaubt. Ursache beheben, bevor der Pull Request aufgemacht wird.
|
|||
|
|
- `neu grün` — schön. Im Pull Request nennen und eine Baseline-Aktualisierung vorschlagen.
|
|||
|
|
- `unverändert gegenüber Baseline` — in Ordnung.
|
|||
|
|
|
|||
|
|
Die Baseline ändert **nur ein Maintainer** mit `--update-baseline`, nach Absprache. Ein Agent fasst sie nicht an.
|
|||
|
|
Das Grünmachen der bekannten roten Tests läuft über eigene Issues, nicht nebenbei.
|
|||
|
|
|
|||
|
|
## 9. Wenn ein Test rot ist
|
|||
|
|
|
|||
|
|
Erst die Frage beantworten: **Ist der Test veraltet oder ist der Code falsch?**
|
|||
|
|
|
|||
|
|
| Befund | Was zu tun ist |
|
|||
|
|
|---|---|
|
|||
|
|
| Der Code verhält sich falsch | Code beheben. Der Test bleibt, wie er ist. |
|
|||
|
|
| Das Verhalten wurde bewusst geändert (Issue deckt es ab) | Test anpassen, im Pull Request begründen, `changelog.txt` prüfen |
|
|||
|
|
| Der Test prüft etwas, das es nicht mehr gibt | Test entfernen — mit Begründung und nur, wenn das Verhalten wirklich entfallen ist |
|
|||
|
|
| `ModuleNotFoundError: sn_basis` | Nicht über `scripts/run_tests.py` gestartet, oder Klone fehlen |
|
|||
|
|
| `AttributeError: _MockQgs… has no attribute` | Mock im Wrapper ergänzen — nicht den Test umgehen |
|
|||
|
|
| Der Test hängt oder braucht Netz | Der Test ist falsch gebaut. Dienstantwort nachstellen. |
|
|||
|
|
|
|||
|
|
**Niemals** ein roter Test wird durch `@unittest.skip` oder durch Löschen grün gemacht. Das ist keine Lösung,
|
|||
|
|
das ist das Verstecken des Problems vor dem nächsten Leser.
|
|||
|
|
|
|||
|
|
Details zum Deuten einzelner Fehler: Skill `sn-tests`.
|
|||
|
|
|
|||
|
|
## 10. Vor dem Pull Request
|
|||
|
|
|
|||
|
|
- [ ] `python scripts/run_tests.py <paket> --baseline` ohne `NEU ROT`
|
|||
|
|
- [ ] Neue Funktionalität hat mindestens einen Test je neuem Verhalten
|
|||
|
|
- [ ] Jeder neue Test hat einen deutschen Docstring `"""Prüft, dass …"""`
|
|||
|
|
- [ ] `python scripts/testkatalog.py` neu erzeugt und mitcommittet
|
|||
|
|
- [ ] Betrifft die Änderung einen dokumentierten Fachablauf: die Seite in `docs/tests/` nachgezogen
|
|||
|
|
- [ ] Bei UI-, Layer-, WFS- oder Layoutverhalten: manueller QGIS-Test durchgeführt und im Pull Request
|
|||
|
|
unter „Wie geprüft" beschrieben — oder ausdrücklich als offener Punkt benannt
|
|||
|
|
|
|||
|
|
## Siehe auch
|
|||
|
|
|
|||
|
|
- Skill `sn-tests` — Tests ausführen und Fehler deuten
|
|||
|
|
- [`docs/konzept/07_qgis_tests_plan.md`](../konzept/07_qgis_tests_plan.md) — Ausbau zu echten QGIS-Tests
|
|||
|
|
- [`docs/entscheidungen/0003-mock-tests-baseline-pflicht.md`](../entscheidungen/0003-mock-tests-baseline-pflicht.md)
|
|||
|
|
- [`docs/entscheidungen/0009-tests-definition-of-done.md`](../entscheidungen/0009-tests-definition-of-done.md)
|
|||
|
|
- [`docs/architektur/schnittstellen.md`](../architektur/schnittstellen.md) — die Wrapper, die den Mock-Modus tragen
|