docs(#134): ADR 0007 — Test-Taxonomie
Phase 3 von #134: Klassifizierung Unit / Integration / E2E / Property / Smoke mit Markern, Latenz-Budgets, Verzeichnis-Konventionen und Lauf-Befehlen. Index aktualisiert (0007 zwischen 0006 und 0008 eingefuegt — ADRs sind chronologisch, nicht numerisch sortiert).
This commit is contained in:
parent
3a8c03db6c
commit
7e20f910fe
120
docs/adr/0007-test-taxonomy.md
Normal file
120
docs/adr/0007-test-taxonomy.md
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
# 0007 — Test-Taxonomie (Unit / Integration / E2E / Property / Smoke)
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| **Status** | accepted |
|
||||||
|
| **Datum** | 2026-04-28 |
|
||||||
|
| **Refs** | #50 (Umbrella E2E Functional Acceptance), #51-54 (Sub-A-D), #134 (Phase 3 Audit), ADR 0003 |
|
||||||
|
|
||||||
|
## Kontext
|
||||||
|
|
||||||
|
Die Test-Suite ist organisch ueber drei Epochen gewachsen:
|
||||||
|
|
||||||
|
1. **Original Unit-Suite** (#46, #91) — `tests/conftest.py` stubbt
|
||||||
|
`fitz`/`bs4`/`openai`/`pydantic_settings`. Lokal in Sekunden lauffaehig,
|
||||||
|
keine externen Calls, keine Live-Daten.
|
||||||
|
2. **E2E Functional Acceptance** (#50 Umbrella) — `tests/integration/`
|
||||||
|
mit eigenem `conftest.py`, das die Stubs *nicht* setzt. HTTP gegen
|
||||||
|
echte Landtags-Portale, PDF-Parsing mit echtem `fitz`, DB-Lookups
|
||||||
|
gegen `embeddings.db`. Marker `integration`.
|
||||||
|
3. **Playwright UI-Tests** (#120) — `tests/e2e/test_ui.py`, headless
|
||||||
|
Chromium gegen die laufende App. Marker `e2e`.
|
||||||
|
|
||||||
|
Mit dem Backfill aus #134 kamen zusaetzlich:
|
||||||
|
|
||||||
|
- **Property-/Substring-Tests** (ADR 0003) fuer LLM-Zitate gegen PDF-Seiten
|
||||||
|
- **Smoke-Tests** (`test_endpoints_smoke.py`) — Endpoints nur auf
|
||||||
|
Antwortcode + Format pruefen, kein Geschaeftslogik-Detail
|
||||||
|
|
||||||
|
Ohne klare Taxonomie weiss niemand, wo ein neuer Test hingehoert. Folge:
|
||||||
|
ad-hoc Tests werden in `tests/` abgelegt, Marker werden vergessen, und
|
||||||
|
beim CI-Lauf brennen langsame Tests die schnellen mit ab.
|
||||||
|
|
||||||
|
## Optionen
|
||||||
|
|
||||||
|
### Option A — Flacher Test-Ordner ohne formale Kategorien
|
||||||
|
|
||||||
|
Status quo bis zu #50: alle Tests unter `tests/`, Marker frei waehlbar.
|
||||||
|
**Vorteile:** keine Migrationskosten, niedrige kognitive Last.
|
||||||
|
**Nachteile:** Kategorien implizit, Stub-Setup kollidiert mit echten
|
||||||
|
Imports, Lauf-Dauer schwankt unvorhersehbar.
|
||||||
|
|
||||||
|
### Option B — Drei harte Verzeichnisse (`tests/unit/`, `tests/integration/`, `tests/e2e/`)
|
||||||
|
|
||||||
|
Strenge raeumliche Trennung mit jeweils eigenem `conftest.py`.
|
||||||
|
**Vorteile:** Stub-Konflikte ausgeschlossen, einfaches `pytest tests/unit/`.
|
||||||
|
**Nachteile:** grosse Migration; viele bestehende Test-Files muessten
|
||||||
|
in `unit/` umziehen; verschachtelte Pfade werden vom Test-Runner und
|
||||||
|
von Reports etwas sperriger.
|
||||||
|
|
||||||
|
### Option C — Flacher Ordner + verbindliche Marker (gewaehlt)
|
||||||
|
|
||||||
|
`tests/` flach, aber **jede** Datei traegt einen klaren Kategorie-Marker
|
||||||
|
(`integration`, `e2e`, `slow`) oder ist Default-Unit. Neue
|
||||||
|
Sub-Verzeichnisse nur wenn sie strukturell notwendig sind (z.B.
|
||||||
|
`tests/integration/` weil dort ein anderer `conftest.py` lebt — keine
|
||||||
|
Stubs).
|
||||||
|
|
||||||
|
**Vorteile:** wenig Migrationsschmerz, Default-Run laeuft schnell, aber
|
||||||
|
opt-in zu langsamen Suiten ist explizit (`pytest -m integration`).
|
||||||
|
**Nachteile:** Disziplin noetig, Marker mssen gepflegt werden.
|
||||||
|
|
||||||
|
## Entscheidung
|
||||||
|
|
||||||
|
**Option C** mit folgender expliziter Taxonomie:
|
||||||
|
|
||||||
|
| Typ | Marker | Verzeichnis | Latenz | Was ist erlaubt |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **Unit** | (none, default) | `tests/*.py` | < 100 ms / Test | Reines Python, alle externen Dependencies gestubbed in `tests/conftest.py`. Domain-Logik, Validatoren, Pure Functions, Datenstrukturen. |
|
||||||
|
| **Smoke** | (none, default) | `tests/test_*_smoke.py` | < 200 ms / Test | TestClient gegen `app.main`, nur Status-Code + Pflicht-Felder pruefen. Skipped wenn `app.main` nicht importierbar. |
|
||||||
|
| **Property** | (none, default) | `tests/test_citations_substring.py` u.a. | < 500 ms / Test | Invarianten-Checks gegen Fixture-Corpus. Substrings, Strukturmuster. PDF-Parsing erlaubt, aber nur gegen Fixtures im Repo. |
|
||||||
|
| **Integration** | `integration` | `tests/integration/` | < 5 s / Test, gesamt < 5 min | Echtes HTTP gegen Landtags-Portale, echtes `fitz` gegen reale PDFs, DB-Lookups gegen `embeddings.db`. Eigenes `conftest.py` ohne Stubs. Opt-in via `pytest -m integration`. |
|
||||||
|
| **E2E** | `e2e` | `tests/e2e/` | < 30 s / Test | Headless-Chromium gegen lokal laufende App oder Prod-URL. Tests koennen flaky sein — werden NICHT von Default-Run getriggert. |
|
||||||
|
| **Slow** | `slow` | (queruerend) | beliebig | Marker-Suffix zu jedem Typ. Ausschliessbar via `pytest -m "not slow"`. Beispiel: ein Integration-Test, der pro BL einen Wahlprogramm-PDF herunterlaedt. |
|
||||||
|
|
||||||
|
**Lauf-Konvention** (verbindlich, in `pytest.ini` definiert):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest # Default — Unit + Smoke + Property, ~1s
|
||||||
|
pytest -m integration # nur E2E-Functional-Acceptance, ~5 min
|
||||||
|
pytest -m "integration and not slow" # E2E ohne PDF-Downloads
|
||||||
|
pytest -m e2e # nur Playwright-UI-Tests
|
||||||
|
pytest -m "" tests/ # ALLES (auch lokal selten gebraucht)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Naming-Konvention:**
|
||||||
|
- `test_<modul>.py` — Unit-Tests fuer ein Modul
|
||||||
|
- `test_<feature>_smoke.py` — Smoke-Tests
|
||||||
|
- `test_<feature>_substring.py` / `_substring_*` — Property-Tests
|
||||||
|
- Integration- und E2E-Tests heissen wie das Feature, das sie testen
|
||||||
|
(z.B. `test_adapters_live.py`, `test_ui.py`).
|
||||||
|
|
||||||
|
## Konsequenzen
|
||||||
|
|
||||||
|
### Positiv
|
||||||
|
|
||||||
|
- Default-Run bleibt schnell (< 2s) — niemand wartet bei jedem Save.
|
||||||
|
- Klar, wo neue Tests landen: jeder neue Test im Default-Ordner ist
|
||||||
|
ein **Unit-Test** mit Stubs; alles, was Live-HTTP/PDF/LLM braucht,
|
||||||
|
geht zwingend nach `tests/integration/`.
|
||||||
|
- CI kann Default-Suite als Pre-Commit-Gate nutzen, Integration-Suite
|
||||||
|
nightly oder pre-deploy.
|
||||||
|
|
||||||
|
### Negativ
|
||||||
|
|
||||||
|
- Disziplin noetig: Marker vergessen → langsame Tests im Default-Run
|
||||||
|
oder unbemerkte Lueckentest. Code-Review muss darauf achten.
|
||||||
|
- Smoke-Tests sind technisch keine Unit-Tests (importieren `app.main`),
|
||||||
|
aber wir behandeln sie wegen geringer Latenz als Default. Ausnahme
|
||||||
|
bewusst akzeptiert.
|
||||||
|
|
||||||
|
### Folgen fuer andere ADRs
|
||||||
|
|
||||||
|
- **ADR 0003** (Sub-D Citation-Property-Tests) bleibt gueltig; Property-Tests
|
||||||
|
sind hier explizit als eigene Kategorie verortet.
|
||||||
|
- Folge-Issue: Coverage-Baseline (`.coveragerc` mit `fail_under` pro
|
||||||
|
Modul) — nicht im Skopus dieses ADRs, sondern eigenstaendig in
|
||||||
|
Phase 3 von #134.
|
||||||
|
- Folge-Arbeit: einzelne bestehende Test-Files umtaggen, falls sie
|
||||||
|
faktisch Integration sind aber als Unit liefen (Audit ergab: keine
|
||||||
|
bekannten Faelle, alle Live-Calls liegen in `tests/integration/`).
|
||||||
@ -23,6 +23,7 @@ und Konsequenzen. Format inspiriert von [Michael Nygard](https://cognitect.com/b
|
|||||||
| [0004](0004-deployment-workflow.md) | Docker Compose Deploy mit DB-/Reports-Volume und SN-XML-Sonderpfad | accepted | 2026-04-10 |
|
| [0004](0004-deployment-workflow.md) | Docker Compose Deploy mit DB-/Reports-Volume und SN-XML-Sonderpfad | accepted | 2026-04-10 |
|
||||||
| [0005](0005-keycloak-sso-with-dev-bypass.md) | Keycloak SSO mit Dev-Bypass-Fallback | accepted | 2026-04-10 |
|
| [0005](0005-keycloak-sso-with-dev-bypass.md) | Keycloak SSO mit Dev-Bypass-Fallback | accepted | 2026-04-10 |
|
||||||
| [0006](0006-embedding-model-migration-v3-to-v4.md) | Embedding-Modell-Migration text-embedding-v3 → v4 | accepted | 2026-04-11 |
|
| [0006](0006-embedding-model-migration-v3-to-v4.md) | Embedding-Modell-Migration text-embedding-v3 → v4 | accepted | 2026-04-11 |
|
||||||
|
| [0007](0007-test-taxonomy.md) | Test-Taxonomie (Unit / Integration / E2E / Property / Smoke) | accepted | 2026-04-28 |
|
||||||
| [0008](0008-ddd-lightweight-migration.md) | DDD-Lightweight-Migration (Repository, LLM-Port, Domain-Verhalten) | accepted | 2026-04-20 |
|
| [0008](0008-ddd-lightweight-migration.md) | DDD-Lightweight-Migration (Repository, LLM-Port, Domain-Verhalten) | accepted | 2026-04-20 |
|
||||||
|
|
||||||
## Wann ADR, wann nicht
|
## Wann ADR, wann nicht
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user