--- inclusion: manual --- # Unit Tests schreiben > **Nutzung:** Referenziere diese Datei mit `#unit-tests-motivation` im Chat und sage: > "Schreibe Unit Tests für [Komponente/Klasse/Funktion] nach diesem Leitfaden." --- ## Ziel Effektive Anleitung für LLMs zur Erstellung qualitativ hochwertiger Unit Tests mit klaren Erwartungen und Best Practices. ## Motivation & Kontext Erhöhe die Unit-Test-Abdeckung im Projekt. - Ziel: Schreibe Unit-Tests (JUnit 5) für das Projekt, sodass die gesamte Code Coverage auf mindestens 80% steigt. - Einschränkungen (nicht ändern): - Verändere keinen Produktionscode unter `src/main/java`. - Verändere keine vorhandenen Tests unter `src/test/java`. - Verändere keine bestehenden Quell-Dateinamen oder APIs. - Keine Netzwerke/DBs in Unit-Tests; externe Abhängigkeiten nur mocken. - Erlaubt: - Neue Testklassen und Test-Hilfsklassen unter `src/test/java` anlegen. - Test-spezifische Ressourcen unter `src/test/resources`. - Anforderungen an Tests: - Nutze JUnit 5 (`@Test`), Moq/Mockito für Abhängigkeiten. - Tests sollen deterministisch, schnell und isoliert sein. - Jede getestete Klasse sollte sinnvolle Positive-/Negative-/Randfall-Tests bekommen. - Benenne Tests nach der vorhandenen Nomenklatur. - Verifizierung (Automatisierte Checks): 1. Build: Projekt baut erfolgreich (Exit-Code 0). Kommando: `mvn test`. 2. Tests: Alle Tests laufen grün (keine Failures/Errors). 3. Coverage: Generiere Coverage-Report (mvn jacoco:report). Akzeptanz: Gesamte Projekt-Coverage >= 80% (Line Coverage). Report-Pfad: `target/site/jacoco/index.html`. - Zusätzliche Hinweise: - Keine Refactorings am Produktionscode. - Falls ein Test-only Helper oder Fixture nötig ist, lege diese ausschließlich unter `src/test/java` an. ## Constraints **Du MUST:** - Tests für alle öffentlichen Methoden/Funktionen schreiben - Sowohl positive als auch negative Testfälle abdecken - Aussagekräftige Testnamen verwenden, die das erwartete Verhalten beschreiben - Arrange-Act-Assert (AAA) Pattern befolgen - Tests isoliert und unabhängig voneinander halten - Mocks/Stubs für externe Abhängigkeiten verwenden **Du SHOULD:** - Edge Cases und Grenzwerte testen - Fehlerbehandlung und Exception-Szenarien abdecken - Parametrisierte Tests für ähnliche Testfälle nutzen - Test-Fixtures und Setup-Methoden sinnvoll einsetzen **Du MUST NOT:** - Tests schreiben, die von externen Ressourcen abhängen (Datenbank, Netzwerk, Dateisystem) - Tests erstellen, die eine bestimmte Ausführungsreihenfolge erfordern - Implementation Details testen statt Verhalten - Produktionscode unter `src/main/java` verändern - Bestehende Tests unter `src/test/java` modifizieren ## Test-Struktur ### Namenskonvention ``` [MethodName]_[Scenario]_[ExpectedBehavior] ``` Beispiele: - `calculateTotal_WithValidItems_ReturnsCorrectSum` - `processPayment_WithInsufficientFunds_ThrowsException` - `getUserById_WhenUserNotFound_ReturnsNull` ### AAA Pattern ```java @Test void testMethodName_Scenario_ExpectedBehavior() { // Arrange - Setup test data and dependencies [Test-Setup] // Act - Execute the method under test [Methodenaufruf] // Assert - Verify the expected outcome [Assertions] } ``` ## Testabdeckung ### Prioritäten 1. **Kritische Geschäftslogik** - Kernfunktionalität mit hohem Risiko 2. **Komplexe Algorithmen** - Berechnungen, Transformationen, Validierungen 3. **Fehlerbehandlung** - Exception-Handling, Validierung 4. **Edge Cases** - Grenzwerte, null/empty, unerwartete Eingaben 5. **Integration Points** - Schnittstellen zu anderen Komponenten ### Mindestabdeckung - **Gesamtprojekt:** 80%+ Line Coverage (Ziel) - **Kritische Komponenten:** 90%+ Code Coverage - **Standard-Komponenten:** 80%+ Code Coverage ## Technologie-spezifische Guidelines ### JUnit 5 + Mockito ```java import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; class ServiceTest { @Mock private Dependency mockDependency; private Service service; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); service = new Service(mockDependency); } @Test void methodName_Scenario_ExpectedBehavior() { // Arrange when(mockDependency.someMethod()).thenReturn(expectedValue); // Act Result result = service.methodUnderTest(); // Assert assertNotNull(result); assertEquals(expectedValue, result.getValue()); verify(mockDependency).someMethod(); } } ``` ## Beispiele ### Beispiel 1: Einfacher Unit Test ```java @Test void add_TwoPositiveNumbers_ReturnsSum() { // Arrange Calculator calculator = new Calculator(); // Act int result = calculator.add(5, 3); // Assert assertEquals(8, result); } ``` ### Beispiel 2: Test mit Mocks ```java @Test void processOrder_ValidOrder_CallsPaymentService() { // Arrange Order order = new Order(100.0); when(mockPaymentService.charge(100.0)).thenReturn(true); // Act boolean result = orderProcessor.process(order); // Assert assertTrue(result); verify(mockPaymentService).charge(100.0); } ``` ### Beispiel 3: Exception Test ```java @Test void divide_ByZero_ThrowsArithmeticException() { // Arrange Calculator calculator = new Calculator(); // Act & Assert assertThrows(ArithmeticException.class, () -> { calculator.divide(10, 0); }); } ``` ## Häufige Fehler vermeiden | Fehler | Lösung | |--------|--------| | Tests testen Implementation statt Verhalten | Fokus auf öffentliche API und erwartete Ergebnisse | | Zu viele Assertions pro Test | Ein Test = Ein Konzept/Verhalten | | Fragile Tests durch harte Abhängigkeiten | Dependency Injection und Mocking nutzen | | Unklare Fehlermeldungen | Aussagekräftige Assert-Messages verwenden | | Tests ohne Arrange-Phase | Immer explizites Setup, auch wenn minimal | | Produktionscode ändern | Nur Tests unter `src/test/java` anlegen | ## Checkliste Vor dem Abschluss prüfen: - [ ] Alle öffentlichen Methoden haben Tests - [ ] Positive und negative Szenarien abgedeckt - [ ] Edge Cases berücksichtigt - [ ] Testnamen sind selbsterklärend - [ ] Tests sind unabhängig und isoliert - [ ] Keine externen Abhängigkeiten - [ ] AAA-Pattern konsequent angewendet - [ ] Code Coverage Ziel (80%) erreicht - [ ] `mvn test` läuft erfolgreich durch - [ ] Kein Produktionscode verändert ## Verifizierung ### Build & Test ausführen ```bash mvn test ``` ### Coverage Report generieren ```bash mvn jacoco:report ``` ### Coverage Report prüfen ```bash # Report-Pfad: target/site/jacoco/index.html # Ziel: >= 80% Line Coverage ``` ## Troubleshooting ### Tests schlagen fehl - Prüfe ob Mocks korrekt konfiguriert sind - Verifiziere Test-Daten und erwartete Werte - Stelle sicher, dass Tests isoliert laufen ### Niedrige Code Coverage - Identifiziere ungetestete Branches mit Jacoco-Report - Füge Tests für Exception-Handling hinzu - Teste Edge Cases und Grenzwerte - Prüfe ob alle öffentlichen Methoden getestet sind ### Langsame Tests - Ersetze echte Abhängigkeiten durch Mocks - Vermeide Thread.sleep() und Timeouts - Nutze In-Memory Implementierungen