:source-highlighter: coderay :icons: font :experimental: :!sectnums: :imagesdir: ./images/ :handout: ./code/ :logo: IT.PROG2 - ifdef::backend-html5[] :logo: image:PROG2-300x300.png[IT.PROG2,100,100,role=right,fit=none,position=top right] endif::[] ifdef::backend-pdf[] :logo: endif::[] ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: :important-caption: :heavy_exclamation_mark: :caution-caption: :fire: :warning-caption: :warning: endif::[] // references :mockito-home: http://mockito.org/[Mockito Homepage] :mockito-docs: http://mockito.github.io/mockito/docs/current/org/mockito/Mockito.html[Mockito Dokumentation] :mockito-annotations: https://www.baeldung.com/mockito-annotations[Konkrete Beispiele mit Mockito] :planet-wissen-herz: https://www.planet-wissen.de/video-das-herz--wie-funktioniert-es-100.html[Planet Wissen] :maven-central: https://search.maven.org/search?q=a:mockito-core[MavenCentral] = {logo} Praktikum Mock-Testing == Einleitung Ziele dieses Praktikums sind: * Anwenden von erweiterten Testverfahren (Mocks, Spys) * Übung des Umgangs mit Mockito === Voraussetzungen * Vorlesung Mock-Testing === Tooling * Installiertes JDK 17+. * Gradle 7.4+ * Mockito ab Version 2. === Struktur Das Praktikum enthält verschiedene Arten von Aufgaben, die wie folgt gekennzeichnet sind: [TU] – Theoretische Übung:: Dient der Repetition bzw. Vertiefung des Stoffes aus der Vorlesung und als Vorbereitung für die nachfolgenden Übungen. [PU] – Praktische Übung:: Übungsaufgaben zur praktischen Vertiefung von Teilaspekten des behandelten Themas. [PA] – Pflichtaufgabe:: Übergreifende Aufgabe zum Abschluss. Das Lösen dieser Aufgaben ist Pflicht. Sie muss bis zum definierten Zeitpunkt abgegeben werden, wird bewertet und ist Teil der Vornote. === Zeit und Bewertung Für das Praktikum stehen zwei Wochen in den Praktikumslektionen und im Selbststudium zur Verfügung. Je nach Kenntniss- und Erfahrungsstufe benötigen Sie mehr oder weniger Zeit. Nutzen Sie die Gelegenheit, den Stoff zu vertiefen, auszuprobieren, Fragen zu stellen und Lösungen zu diskutieren (Intensive-Track). Falls Sie das Thema schon beherrschen, müssen Sie nur die Pflichtaufgaben lösen und bis zum angegebenen Zeitpunkt abgeben (Fast-Track). Sie können die Lösungen der Textaufgaben in diesem Praktikum direkt in den Java-Files als Kommentar hinzufügen - oder als Markdown-File neben den Java-Files. === Referenzen * link:{handout}[Praktikumsverzeichnis – Quellcode, Projektstruktur] * link:{mockito-home} * link:{mockito-docs} * link:{mockito-annotations} :sectnums: :sectnumlevels: 2 // Beginn des Aufgabenblocks == Herz Das menschliche Herz ist ein lebenswichtiges und kompliziertes Organ. Nur, wenn seine einzelnen "Bauteile" richtig koordiniert werden, kann es eine ausreichende Blutversorgung unseres Körpers gewährleisten. Da es beim Mocking um das Testen des Verhaltens eines Objekts geht, bietet sich das Herz als Beispiel an. Darum simulieren wir in dieser Aufgabe ein menschliches Herz. Eine kurze Erklärung der Funktionsweise des Herzens gibt es auf link:{planet-wissen-herz}. Einige Fakten über das Herz, die bei dieser Aufgabe helfen können - im Projekt werden englische Begriffe verwendet, die Sie in Klammern finden: * Das Herz (Heart) besteht aus zwei Hälften. * Jede Herzhälfte (Half) hat einen Vorhof (Atrium) und eine Herzkammer (Ventricle). Vorhöfe sammeln Blut, Herzkammern pressen es in den Körper. * Zwei Phasen wechseln sich jeweils ab. ** Anspannungsphase: Systole ** Entspannungsphase: Diastole * Die Herzklappen sind eine Art Rücklaufventile, es gibt 2 verschiedene: ** 2 Segelklappen (AtrioventricularValve): verhindern, dass Blut in der Systole in den Vorhof zurückfliesst. ** 2 Taschenklappen (SemilunarValve): verhindern, dass in der Diastole Blut ins Herz zurückfliesst. image:Heart_diagram-en.svg.png[pdfwidth=75%, width=600px] === Ablauf des Herzschlags ==== Diastole – Blut sammelt sich im Vorhof ("atrium") * Segelklappen ("atrioventricular valves") öffnen * Taschenklappen ("semilunar valves") schliessen * Vorhöfe ("atria") entspannen * Herzkammern ("ventricle") entspannen * Warten auf Systole ==== Systole * Segelklappen schliessen * Taschenklappen öffnen * Herzkammern kontrahieren * Vorhöfe kontrahieren * Warten auf Diastole Es gibt die folgenden Klassen im Projekt: image:heartbeat_classdiagram.png[pdfwidth=75%, width=600px] Darüber hinaus gibt es eine Klasse `HeartTest`, die bereits mehrere Tests implementiert. Stubs sind nicht sehr intelligent und daher kann man häufig nur oberflächlich mit Stubs testen. Sie sollen mit Hilfe von Mock-Objekten die Tests noch gründlicher gestalten. Die Abhängigkeit für Mockito ist bereits für Sie in build.gradle erfasst worden. Studieren Sie kurz in diesem File, wie das gemacht wurde. Informationen dazu finden Sie auf der link:{mockito-home} und auf link:{maven-central}. Studieren bzw. überfliegen Sie bei dieser Gelegenheit auch gerade die link:{mockito-docs}. == Aufgaben === Einführung in Mockito [PU] [loweralpha] . Studieren Sie die Testmethoden `HeartTest::testValveStatus()` und `HeartTest::testExecuteHartBeatErrorBehaviour()`. Dort wird *ein Teil* des Verhaltens des Herzens getestet. . Implementieren Sie die Methoden `Heart::executeDiastole()`, `Heart::executeSystole()` und `Heart::executeHeartBeat()` sodass die bestehenden Tests durchlaufen und das oben beschriebene Verhalten des Herzens modelliert wird. . (optional) Ein echtes Herz hat eine Schlagfrequenz. Implementieren Sie, dass das Herz nach jeder Systole pausiert. Sie können z. B. den aktuellen Thread 1000 ms lang anhalten mit + [source, Java] ---- try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } ---- === Fragen zu Testing [TU] [loweralpha] . Testing kann in zwei unterschiedliche Strategien aufgeteilt werden. Zum einen gibt es White-Box Testing und zum zweiten Black-Box Testing. Was für Java Libraries gibt es um diese zwei Strategien zu testen? Wann wenden Sie welche Strategie an? . Das Erstellen von guten automatisierten Unit-Tests kann manchmal schwierig umzusetzen sein. Was ist der Hauptgrund dafür? Wie können Sie dieses Problem entschärfen? . Der Testfokus war bisher auf der Klasse `Heart`. Testen Sie jetzt die Klasse `Half`. Wo verwenden Sie Stubbing, wo Mocking? === Vorbedingungen, Exceptions und Callback-Funktionen [PA] [loweralpha] . Testen Sie, dass beim Ausführen der Diastole und Systole alle Herzklappen in der richtigen Reihenfolge geöffnet und geschlossen werden. Implementieren Sie dafür die Methode `HeartTest::testValvesBehavior()` und benutzen Sie die von Mockito bereitgestellte Klasse `InOrder`. + Testen Sie auch, dass keine Methode unnötig oft aufgerufen wird (mit der Methode `Inorder::verifyNoMoreInteractions()`). + Benutzen Sie mindestens einmal die statische Methode `mock()` und einmal die Annotation `@Mock` von Mockito. . Die vorhandene Implementation des Testfalls `HeartTest::testExecuteHartBeatErrorBehaviour()` verwendet keine Mock-Objekte für die linke und rechte Herzhälfte. Implementieren Sie die Testmethode `HeartTest::testExecuteHartBeatErrorBehaviourWithStubbing()` analog zur bestehenden Variante, aber diesmal setzen Sie entsprechende Mock-Objekte für die Herzhälften ein. + TIP: Für das Stubbing werden Sie die `doThrow`- Methode von Mockito benötigen. Mit Stubbing ist die Konfiguration von Ihrem Mock-Objekt gemeint. . Die bestehende Implementierung hat einen ungenügenden Exception-Mechanismus. Beispielsweise führt die Herzhälfte (`Half`) ihre Methoden aus, auch wenn die notwendigen Vorbedingungen (d.h., die Klappenstellungen von `SemilunarValve` und `AtrioVentricularValve`) nicht gelten. + Beheben Sie dieses Problem, indem Sie in `Heart::executeDiastole()` und `Heart::executeSystole()` eine `InvalidValvePositionException` werfen, falls die notwendigen Vorbedingungen nicht in der korrekten Position (offen, geschlossen) sind. + Erstellen Sie zwei Methoden `testDiastoleException()` und `testSystoleException()` und setzen Sie Mocking und Stubbing ein, um diesen Exception-Mechanismus zu testen. . Die Klasse Pacemaker ist eine Implementation für einen Herzschrittmacher. Es ist eine unvollständige Implementation, reduziert auf das Wesentliche für den Inhalt des Praktikums. Ihre Aufgabe ist es, die Methode `Pacemaker::setHeartRate` mit zwei vorgegeben Testfällen zu testen. + Implementieren Sie Test-Methoden `PacemakerTest::testSetHeartRateRejectsFrequenciesOutOfRange` und `PacemakerTest::testSetHeartRateAppliesFrequenciesInsideRange`. + Die beiden Testfälle prüfen die Methode `Pacemaker::setHeartRate` darauf, ob sie korrekt reagiert, wenn das Herz die gewünschte Frequenz im einen Fall anwenden und im anderen Fall nicht anwenden kann. In diesem Szenario ist die Klasse `Pacemaker` die Class-Under-Test und die Klasse `Heart` eine Abhängigkeit für `Pacemaker`. + Verwenden Sie für die Klasse `Heart` ein entsprechendes Mock-Objekt und stubben Sie das Verhalten gemäss der Spezifikation von der Methode `Heart::setHeartRate()`. Eine Voraussetzung ist, dass Sie für beide Testfälle dasselbe Mock-Objekt mit demselben Stubbing verwenden können. Die Callback-Methode wird Ihnen dabei eine Hilfe sein. == Abschluss Stellen Sie sicher, dass die Tests der Pflichtaufgabe mittels `gradlew test` gestartet werden können und pushen Sie die Lösung vor der Deadline in Ihr Abgaberepository.