meldestelle/docs/06_Frontend/Teststrategie_Desktop.md

6.2 KiB
Raw Blame History

🧐 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
  1. 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
  1. 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)

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:

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)