Mark A-1 as complete: Add detailed QA Test-Strategie for Compose Desktop App, including test pyramid, tooling, conventions, and CI/CD integration; link roadmap and strategy documents.

This commit is contained in:
Stefan Mogeritsch 2026-04-02 18:43:09 +02:00
parent 4ae701d969
commit 2715b75535
2 changed files with 190 additions and 5 deletions

View File

@ -7,11 +7,12 @@
## 🔴 Sprint A — Sofort (diese Woche)
- [ ] **A-1** | Test-Strategie für Desktop-App definieren
- [ ] Testpyramide für Compose Desktop festlegen (Unit / Integration / UI-Tests)
- [ ] Tooling entscheiden: `kotlin.test`, Compose UI Test, Mockk
- [ ] Test-Konventionen dokumentieren (Namensschema, Ordnerstruktur, Arrange-Act-Assert)
- [ ] Dokument in `docs/06_Frontend/` oder `docs/07_Infrastructure/` ablegen
- [x] **A-1** | Test-Strategie für Desktop-App definieren — siehe „QA Test-Strategie — Compose Desktop App“
- [x] Testpyramide für Compose Desktop festlegen (Unit / Integration / UI-Tests)
- [x] Tooling entscheiden: `kotlin.test`, Compose UI Test, Mockk
- [x] Test-Konventionen dokumentieren (Namensschema, Ordnerstruktur, Arrange-Act-Assert)
- [x] Dokument in `docs/06_Frontend/` oder `docs/07_Infrastructure/` ablegen
- 📄 Dokument: [`docs/06_Frontend/Teststrategie_Desktop.md`](../../06_Frontend/Teststrategie_Desktop.md)
---

View File

@ -0,0 +1,184 @@
# 🧐 QA Test-Strategie — Compose Desktop App
> Stand: 2. April 2026
> Gültig für: Kotlin Multiplatform / Compose Desktop (JVM) Frontend
---
## Ziel
Konsistente, schnelle und verlässliche Tests für die Desktop-App. Diese Richtlinie definiert die Testpyramide, das Tooling und verbindliche Konventionen (Benennung, Ordnerstruktur, ArrangeActAssert), damit Frontend, QA und DevOps nahtlos zusammenarbeiten können.
---
## Testpyramide für Compose Desktop
1) Unit-Tests (7080%)
- Reine Kotlin-Logik ohne UI: ViewModels, Reducer/Intent-Handler, Mapper/Formatter, Validatoren
- Keine echten I/OOperationen, keine echten Uhren/Dispatcher → alles gemockt oder gefaked
- Laufzeit: < 100 ms pro Test, parallelisierbar
2) Integrations-Tests (1525%)
- Zusammenspiel mehrerer Komponenten auf JVM (z. B. ViewModel + Repository mit InMemory/Fake Datenquelle)
- Optionale Nutzung eines Test-Dispatchers; kein echtes Netzwerk/Dateisystem
- Ziel: Korrekte State-Transitions, Fehlerpfade, Laden/SpeichernFlows
3) UI-Tests (510%)
- Compose UI Test Framework (Desktop): Semantics-basierte Interaktion und Assertions
- Abdecken kritischer User-Flows (Happy Path + 12 Edge Cases)
- Headless-fähig für CI (Xvfb/JetBrains Runtime Headless). Kürzere, robuste Tests (keine Pixel-Assertions)
Leitprinzip: So viel wie möglich unterhalb der UI testen (schnell/stabil), nur kritische End-to-EndFlows als UI-Test absichern.
---
## Tooling-Entscheidung
- Test-Framework: `kotlin.test`
- Einheitlich über KMP, minimaler Overhead, gute IDE/CI-Integration
- Mocking/Stubs: `MockK`
- Kotlinfreundlich, unterstützt Klassen/Objekte, klare Verifikation von Interaktionen
- Coroutines/Flows testen: `kotlinx-coroutines-test`
- `StandardTestDispatcher`, `runTest {}`, virtuelle Zeitsteuerung, deterministische Tests
- UI-Tests: Compose UI Test (Desktop)
- `compose.ui.test` APIs: `onNodeWithText`, `performClick`, `assertIsDisplayed`, etc.
- Headless in CI via JVM Args/Virtual Display (DevOps setzt Runner bereit)
- Optional (nur falls notwendig):
- Snapshot-Testing vermeiden (fragil im Desktop-Kontext)
- Property-based Testing optional (z. B. mit Kotest property) — nicht Standard
---
## Ordner- und Modulstruktur (KMP/Compose Desktop)
Beispielhaft; bitte auf bestehende Modulnamen im Repo mappen:
- `frontend/` (Wurzel des KMP-Frontends)
- `commonMain/` — UI-agnostische Logik, Models, Use-Cases, Validatoren
- `commonTest/` — Unit- und Integrations-Tests für Common
- `desktopMain/` — Desktop-spezifische UI (Compose Desktop) und Integrationscode
- `desktopTest/` — UI-Tests (Compose UI Test) und Desktop-Integrations-Tests
Konvention:
- Business-/State-Logik so weit wie möglich in `commonMain` halten → maximaler Anteil schneller Unit-Tests in `commonTest`.
- UI-spezifische Tests in `desktopTest` nur für kritische End-to-EndFlows.
---
## Benennungs- und Strukturkonventionen
- Test-Klassenname: `<ProduktionsKlasse>Test.kt` oder `<Feature/UseCase>Test.kt`
- Test-Funktionsname (kotlin.test): ``fun `<Methode/Intent>_<Bedingung>_<ErwartetesVerhalten>()``
- Beispiel: ``fun `onSave_withInvalidInput_emitsValidationError`()``
- ArrangeActAssert (AAA) strikt einhalten:
- Arrange: Testdaten, Mocks, System Under Test (SUT) erstellen
- Act: eine gezielte Aktion / Intent ausführen
- Assert: erwarteten Zustand/Ereignisse prüfen
- Given/When/Then als Kommentare optional:
- `// Given`, `// When`, `// Then` — keine unnötigen Kommentare, nur zur Struktur
- Ordner nach Domäne/Feature gruppieren (bevorzugt):
- `commonTest/<feature>/...`, `desktopTest/<flow>/...`
---
## Coroutines & State-Tests (Beispiel)
```kotlin
import kotlin.test.*
import kotlinx.coroutines.test.*
class AnmeldungViewModelTest {
@Test
fun `onWeiter_withEmptyPflichtfeld_emitsValidationError`() = runTest {
// Arrange
val vm = AnmeldungViewModel(/* fakes */)
// Act
vm.onWeiter()
// Assert
assertTrue(vm.state.value.validationErrorShown)
}
}
```
---
## Compose UI Test (Desktop) — Grundsätze
- Selektoren über Semantics (Text, ContentDescription, TestTags via `Modifier.testTag("…")`)
- Keine Fragilität: Keine Pixel-/Layoutabhängigen Asserts
- Jeder UI-Test prüft genau einen kritischen Flow, Laufzeitziel < 3s pro Test
Minimalbeispiel:
```kotlin
import androidx.compose.ui.test.*
import kotlin.test.Test
class NennungUiTest {
@Test
fun startliste_filterByBewerb_showsOnlyMatchingEntries() {
// Given
val rule = createComposeRule()
rule.setContent { AppRoot() }
// When
rule.onNodeWithTag("filter-bewerb").performClick()
rule.onNodeWithText("CSN-C-NEU 95cm").performClick()
// Then
rule.onAllNodesWithTag("startlisten-zeile")
.assertCountEquals( /* erwartete Anzahl */ 5)
}
}
```
Hinweis: Die konkrete Setup-/Runner-Konfiguration für Headless-Ausführung wird in `docs/07_Infrastructure/ci-testing.md` (DevOps) dokumentiert.
---
## Testdaten und Fakes
- Fakes statt Mocks, wenn Verhalten wichtiger als Interaktion ist (z. B. InMemoryRepository)
- Test-Datenbuilder verwenden (kleine DSLs / Fabriken) statt anonymer Maps/Listen
- Zeitabhängiges Verhalten: `TestCoroutineScheduler` + injizierbare Clock/NowProvider
---
## Qualitätsregeln für Tests
- Stabilität > Vollständigkeit: flaky Tests sind zu entfernen oder neu zu schneiden
- Ein Test ein Verhalten: keine überladenen Tests
- Determinismus: keine versteckten Sleeps/Delays, virtuelle Zeit nutzen
- Lesbarkeit: AAA, sprechende Namen, knappe ArrangeBlöcke
---
## CI/CDIntegration (Kurz)
- Unit/Integrations: JVM headless, parallel (Gradle `--parallel`), `-Dkotlinx.coroutines.scheduler.keep.alive.sec=…` falls nötig
- UI: Headless via Xvfb oder JBR Headless; kurzer SmokeSatz ausführen
- Reports: Gradle HTML/JUnit XML; Flaky-Tracking über CI möglich
---
## Checkliste (DoD)
- [ ] Neuer Test folgt AAA und Namenskonvention
- [ ] Läuft headless lokal und in CI
- [ ] Keine externen Abhängigkeiten ohne Fake/Mock
- [ ] UI-Test nutzt Semantics/TestTags und ist robust
---
## Verweise
- Roadmap: `docs/04_Agents/Roadmaps/QA_Roadmap.md` (Punkt A-1)
- DevOps (Headless/CI): `docs/07_Infrastructure/` (geplantes Dokument `ci-testing.md`)