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:
@@ -7,11 +7,12 @@
|
|||||||
|
|
||||||
## 🔴 Sprint A — Sofort (diese Woche)
|
## 🔴 Sprint A — Sofort (diese Woche)
|
||||||
|
|
||||||
- [ ] **A-1** | Test-Strategie für Desktop-App definieren
|
- [x] **A-1** | Test-Strategie für Desktop-App definieren — siehe „QA Test-Strategie — Compose Desktop App“
|
||||||
- [ ] Testpyramide für Compose Desktop festlegen (Unit / Integration / UI-Tests)
|
- [x] Testpyramide für Compose Desktop festlegen (Unit / Integration / UI-Tests)
|
||||||
- [ ] Tooling entscheiden: `kotlin.test`, Compose UI Test, Mockk
|
- [x] Tooling entscheiden: `kotlin.test`, Compose UI Test, Mockk
|
||||||
- [ ] Test-Konventionen dokumentieren (Namensschema, Ordnerstruktur, Arrange-Act-Assert)
|
- [x] Test-Konventionen dokumentieren (Namensschema, Ordnerstruktur, Arrange-Act-Assert)
|
||||||
- [ ] Dokument in `docs/06_Frontend/` oder `docs/07_Infrastructure/` ablegen
|
- [x] Dokument in `docs/06_Frontend/` oder `docs/07_Infrastructure/` ablegen
|
||||||
|
- 📄 Dokument: [`docs/06_Frontend/Teststrategie_Desktop.md`](../../06_Frontend/Teststrategie_Desktop.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -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, Arrange–Act–Assert), damit Frontend, QA und DevOps nahtlos zusammenarbeiten können.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testpyramide für Compose Desktop
|
||||||
|
|
||||||
|
1) Unit-Tests (70–80%)
|
||||||
|
- Reine Kotlin-Logik ohne UI: ViewModels, Reducer/Intent-Handler, Mapper/Formatter, Validatoren
|
||||||
|
- Keine echten I/O‑Operationen, keine echten Uhren/Dispatcher → alles gemockt oder gefaked
|
||||||
|
- Laufzeit: < 100 ms pro Test, parallelisierbar
|
||||||
|
|
||||||
|
2) Integrations-Tests (15–25%)
|
||||||
|
- Zusammenspiel mehrerer Komponenten auf JVM (z. B. ViewModel + Repository mit In‑Memory/Fake Datenquelle)
|
||||||
|
- Optionale Nutzung eines Test-Dispatchers; kein echtes Netzwerk/Dateisystem
|
||||||
|
- Ziel: Korrekte State-Transitions, Fehlerpfade, Laden/Speichern‑Flows
|
||||||
|
|
||||||
|
3) UI-Tests (5–10%)
|
||||||
|
- Compose UI Test Framework (Desktop): Semantics-basierte Interaktion und Assertions
|
||||||
|
- Abdecken kritischer User-Flows (Happy Path + 1–2 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-End‑Flows als UI-Test absichern.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tooling-Entscheidung
|
||||||
|
|
||||||
|
- Test-Framework: `kotlin.test`
|
||||||
|
- Einheitlich über KMP, minimaler Overhead, gute IDE/CI-Integration
|
||||||
|
|
||||||
|
- Mocking/Stubs: `MockK`
|
||||||
|
- Kotlin‑freundlich, 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-End‑Flows.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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`()``
|
||||||
|
|
||||||
|
- Arrange–Act–Assert (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-/Layout‑abhä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/Now‑Provider
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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 Arrange‑Blöcke
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CI/CD‑Integration (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 Smoke‑Satz 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`)
|
||||||
Reference in New Issue
Block a user