# Implementing for Maintainability ## Motivation and Introduction - Unit und Integration Testing ist ein bekannter Ansatz um Qualität sicherzustellen - Gut-getestete Anwendungen: - 1 line of code = 1-3 lines of test code - kann auf bis zu 1:10 hochgehen - Produkt- und Testcode werden parallel geschrieben - guten Code schreiben ist schwer ## Black-/White-Box Testing ![image_555.png](image_555.png) ### Black-Box-Testing > Nur das Interface ist bekannt, nicht der Inhalt - Methode vom Software-Testing, das die Funktion analysiert, ohne den Mechanismus zu kennen - Normalerweise rund um die Spezifikationen und Anforderungen - _Was soll die Anwendung tun, nicht wie tut sie es_ ### White-Box-Testing > Das Interface und alle Mechanismen sind bekannt - Testmethodik, die verifiziert, wie die Anwendung arbeitet - Tests sind am Sourcecode ausgerichtet, nicht an den Anforderungen ### Pros / Cons - [White-Box-Testing](01_ImplementingForMaintainability.md#white-box-testing) ist systematischer und anspruchsvoller - Analyse des Codes kann zu Fehlerentdeckungen führen, die zuvor übersehen wurden - Testergebnisse sind oft spröde - Sind sehr verknüpft mit der Implementierung des Codes - Solche Tests produzieren viele false-positives und sind nicht gut für die Metrik der Resistenz gegen Refactoring - Können häufig nicht rückgeschlossen werden zu einem Verhalten, dass wichtig ist für eine Business-Person - Starkes Zeichen, dass die Tests nicht viel Wert hinzufügen - [Black-Box-Testing](01_ImplementingForMaintainability.md#black-box-testing) hat gegensätzliche Vor-/Nachteile > Black-/White-Box-Testing sind Konzepte, die auf verschiedene Test-Typen angewendet werden können ## Testing Quadrants Matrix ![image_556.png](image_556.png) ### Quadrant 1: Technologie-fokussierte Tests, die das Development leiten - Developer Tests: - Unit tests - Verifizieren Funktionalität eines kleinen Subsets des Systems - Component-/Integration Tests: - Verifizieren Verhalten eines größeren Teils - Tests sind nicht für den Kunden ## Unit Testing - Unit Tests sind die essenzielle Basis einer guten Test-Suite - Verglichen mit anderen, sind sie einfach zu erstellen und warten - Viele Unit-Tests :) ## Goals of Testing during Implementation ### Aktiviere nachhaltiges Wachstum des Software-Projekts - ![image_579.png](image_579.png) - Nachhaltigkeit ist wichtig - Projektwachstum ist am Anfang einfach - Das Wachstum zu halten ist schwer - Tests können das nachhaltige Wachstum fördern - Aber: erfordern initialen, teilweise signifikanten Einsatz - Schlechte Tests bringen nichts - verlangsamen am Anfang schlechten Code - langfristig trotzdem ungünstig - Test Code ist Teil der Codebase - Teil, der ein spezifisches Problem behandelt - Anwendungsrichtigkeit sicherstellen - Kosten eines Tests - Wert - anstehende Kosten - _Refactoring des Tests, wenn der Code refactored wird_ - _Test bei jeder Codeänderung ausführen_ - _Mit Fehlalarmen durch den Test umgehen_ - _Zeit für das Verstehen des Tests, wenn man den zu testenden Code verstehen möchte_ ### General Testing Strategy #### Testing Automation Pyramid ![image_580.png](image_580.png) - Software Tests in 3 Kategorien aufteilen - **UI-Tests** - Testen die Anwendung durch Interaktion mit der UI - sehr high-level - **Service Tests** - [Black-Box](#black-box-testing) testen von größeren Softwareteilen - _bspw. Komponenten, Services_ - **Unit Tests** - werden während Entwicklung von Developern geschrieben - Gibt Idee, wie viele Tests pro Kategorie in der Test-Suite sein sollten #### Testing Ice Cream Cone ![image_581.png](image_581.png) - Style einer Test-Suite, die häufig in der Industrie verwendet wird - hohe Anzahl manueller, high-level Tests - end-to-end Tests (auch an UI), die automatisch ausgeführt werden können - nur wenige integration/unit tests - Tests Suite mit dieser Strategie ist nicht gut wartbar - manuelle Tests sind teuer und langwierig - automatisierte high-level Tests gehen häufig kaputt, sobald Änderungen in der Anwendung auftreten ### Test Driven Development (TDD) ![image_582.png](image_582.png) 1. Tests schreiben - **Design** - Akzeptanzkriterien für den nächsten Arbeitsschritt festlegen - Anregung [lose gekoppelte Komponenten](ImplementingForMaintainability.md#loose-coupling) zu entwerfen - einfache Testbarkeit, dann verbinden - Ausführbare Beschreibung von dem was der Code tut - **Implementierung** - Vervollständigen einer regressiven Test-Suite 2. Tests ausführen - **Implementierung** - Error erkennen, während der Kontext noch frisch ist - **Design** - Bewusstmachung, wann die Implementierung vollständig ist > Golden Rule of TDD: schreibe niemals neue Funktionalitäten ohne einen fehlschlagenden Test #### Vorteile des TDD - signifikante Reduktion der Defekt-Rate - auf Kosten eines moderaten Anstiegs im initialen Development-Prozesses - Empirische Untersuchungen haben das noch nicht bestätigt - TDD hat aber zu Qualitätssteigerung des Codes geführt #### Häufige Fehler beim TDD - Individuelle Fehler - _Vergessen die Tests regelmäßig auszuführen_ - _Zu viele Tests auf einmal schreiben_ - _Zu große/grobe Tests schreiben_ - _zu triviale Tests schreiben, die eh funktionieren_ - Team-Fehler - _Nur partieller Einsatz_ - _Schlechte Wartung der Test-Suite_ - _Verlassene Test-Suite (nie ausgeführt)_ ## Unit-Testing vs. Integration Testing | Unit | Integration | |-------------------------------|----------------------| | kleiner Teil eines Verhaltens | größere Portion Code | ![image_583.png](image_583.png) ### 4 Typen von Produktions-Code ![image_584.png](image_584.png) #### Dimensionen ##### Komplexität und Domain-Signifikanz - Code Komplexität - Definiert durch Nummer der Branching-Punkte im Code - _if-statements, Polymorphismus_ - Domain-Signifikanz - Wie signifikant ist der Code für die problematische Domain - normalerweise Verbindung Domain-Layer-Code zu End-User-Ziele - Hohe Domain-Signifikanz - _Bsp. für niedrige Relevanz: Utility Code_ ##### Nummer der Kollaborateure - Kollaborateur = Abhängigkeit, die veränderlich /& außerhalb des Prozesses ist - veränderlich - nicht nur read-only - außerhalb des Prozesses - häufig geteilt - hindert Tests an unabhängiger Ausführung - Code mit vielen Kollaborateuren ist schwer zu testen #### Typen ##### Domain model & algorithms - **Domain Code, wenige Kollaborateure** - komplexer Code häufig Teil des Domain-Models - wenige Kollaborateure → Testbarkeit - sollte NIEMALS Abhängigkeiten außerhalb des Prozesses haben ##### Controllers - **Wenig Domain Code, viele Kollaborateure** - Koordination der Ausführung von Use-Cases für Domain-Klassen und externen Anwendungen - Keine komplexe / Business-kritische Arbeit selbst/allein machen - wenig Komplexität, wenig Domain-Signifikanz - Viele Abhängigkeiten außerhalb des Projekts ##### Overcomplicated Code - **Viel Domain Code, viele Kollaborateure** - ist aber auch komplex und wichtig ##### Trivialer Code - **wenig Domain-Code, wenig Kollaborateure** #### Separierung von Controllers & DomainModel, Algorithmen ![image_585.png](image_585.png) - Separiert komplexen Code von Code, der Orchestrierung macht - Domain Code hat tiefe Implementierung in Business Logik - Controller haben breite Orchestrierung aber enge Komplexität - Erhöht Wartbarkeit und Testbarkeit #### Wie testet man die 4 Typen? ![image_587.png](image_587.png) - trivialer Code muss nicht getestet werden