Ich nehme derzeit an einer Akademie zum Java Fullstack Software Engineer teil. In den kommenden Wochen möchte ich hier meine Mitschrift, so gut es geht, aufzeichnen und mitteilen. Hier ist das, was ich vom dritten Tag in Block 4 gelernt und behalten habe:
Lifecycle
Wir erstellen ein neues Projekt ueb-junit5-param und eine neue Klasse Arithmetic:

Dann erzeugen wir die dazugehörige Testklasse:

Kurze Wiederholung der Reflection Api
Für die nächsten Schritte wiederholen wir noch mal die Reflection-Api (Siehe auch HolzofenProjekt HKW03):
Die Reflection-Api (liegt im Classloader) erzeugt Instanzen indem sie (von ausserhalb der Runtime) Instanzen anstupst.
Der Classloader lädt die Klassen. Static-Code wird in den Non-Heap-Speicher geladen. Die Instanzen werden in den Heap-Speicher geladen.
Das Pendant des "new", welches zur Compile-Zeit eine Instanz erzeugt, ist zur Laufzeit (Dank Reflection-Api) die Reihenfolge Class.forName(…).getConstructor(…).newInstance(….)
Class.forName("de.firma.util.Test").getConstructor(Signatur(Typen)).newInstance(Werte)

D.h. der Test wird ausgeführt, wenn der Classloader sie lädt.
@BeforeAll und @AfterAll muss static sein. Diese beiden Methoden müssen außerhalb des Heaps liegen, um Vorbereitungs- bzw. Aufräumarbeiten durchführen zu können. JUnit ist verantwortlich für die Tests. Die Annotationen werden von den IDEs (IntelliJ, Eclipse), aber auch Maven interpretiert.

Man kann auch mit Maven testen:


Konstruktoren werden normalerweise nicht gebraucht, da der Default-Konstruktor verwendet wird. Sobald ich mehrere Tests mache, sollte ich Konstruktoren verwenden. Diese müssen in @BeforeEach oder @AfterEach gebaut werden:

Wir testen erst mal ohne Parameter. Wir sehen, in welcher Reihenfolge die Tests durchgeführt werden:

Für parametrisierte Tests brauchen wir eine weitere Bibliothek in der pom.xml:
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <version>5.8.2</version> <scope>test</scope> </dependency>
Wir erstellen folgende Test-Methode:

Übung: Body Mass Index
ueb02-testBMI
BMI=Gewicht /Größe/Größe
- https://herzbewegt.org/bmi/
Aufgabe: Erstelle folgendes: - JUnit 5
- UtilityKlasse
-BMI
-Kategoriesierung ** - UtilityTestKlasse
- Vorbereitung / Nachbereitung
-Tests
-bmi's - Exception
- mit Parametern
- Vorbereitung / Nachbereitung
Ergebnis:

Bmi.java
package de.firma.util;public class Bmi {
public double bmiBerechnen (double groesse, double gewicht) throws GroesseException {
double bmi;
if(groesse<=0){
throw new GroesseException("Division by zero or less");
}
bmi = gewicht/groesse/groesse100100;
return bmi ;
}public static Klassifizierung klassifizierung(double bmi) throws GroesseException{ Bmi.klassifizierung(bmi); if (bmi < 18.5) { return Klassifizierung.UNTERGEWICHT; } if (bmi < 25.0) { return Klassifizierung.NORMAL; } if (bmi < 30) { return Klassifizierung.UEBERGEWICHT; } if (bmi < 35) { return Klassifizierung.ADIPOSITAS; }else return Klassifizierung.ADIPOSITAS; }
}
BmiTest.java
package de.firma.util;import org.junit.Test;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;public class BmiTest {
Bmi bmi = new Bmi(); @Test public void testBmi() { System.out.println("Test Größe kleiner gleich 0"); //assertEquals(22.69,bmi.bmiBerechnen(183,76),0.1); assertThrows(GroesseException.class, () -> { bmi.bmiBerechnen(0, 1); }); } @Test public void testBmi2() throws GroesseException { System.out.println("Test BMI"); assertEquals(22.69, bmi.bmiBerechnen(183, 76), 0.1); } @ParameterizedTest @CsvSource({"183,76,22.7", "190,90,24.9", "192,180,48.8", "0,76,22.7"}) public void testBmi3(double groesse, double gewicht, double expectedResult) throws GroesseException { System.out.println("Test BMI mit Parametern"); assertEquals(expectedResult, bmi.bmiBerechnen(groesse, gewicht), 0.1); }
}
Klassifizierung.java (enum)
package de.firma.util;public enum Klassifizierung {
UNTERGEWICHT,
NORMAL,
UEBERGEWICHT,
ADIPOSITAS
}
GroesseException.java
package de.firma.util;public class GroesseException extends Exception{
public GroesseException(String msg){
super(msg);
}
}
Dem aufmerksamen Leser mag aufgefallen sein, dass wir hier gar keine app.java Klasse aufgelistet haben. Die brauchen wir für die Tests auch gar nicht. Hiermit sind wir zum ersten Mal ohne die mainKlasse ausgekommen.
Mocking, Attrapen, Simulation von Intelligenz
Wir unterscheiden zwischen folgenden Möglichkeiten der Simulation:
- Dummyklassen
- Int getSteuerklasse () {return 1}
- Stubklasse (Stellvertreterklasse)
- Int getSteuerklasse () {return Math.Random [1-5]}
- MockKlasse (Attrape)
- Int getSteuerklasse (); // Interface oder abstrakte Klasse
- Framework Mokito
○ @Mock -> Erzeugung der Attrappe
○ @Inject Mock -> Einbindung der Attrappe
o When -> Definition des Verhaltensmeiner Methode -> Parameter
Verify -> Überprüfung ob bestimmte Methodenaufrufe erfolgt sind

Ich erstelle ein Objekt von etwas, wo ich nur ein Interface von habe:

Interface:

Jetzt werden die Aktien simuliert. Mein Portfolio besitzt jetzt einen Service, der eine Seifenblase ist und ein Array von Aktien. Damit kann ich meinen Marktwert simulieren.
Konkret: Nachdem ich das Interface gebaut habe, erstelle ich davon ein Objekt davon, hier im Beispiel "stockService". Dieses Objekt belege ich mit einem Dummy-Wert aus dem mock-Befehl.
Mit dem "when"-Befehl, kann ich diesen "Wert" abrufen und ihn mit einem echten (Test-)Wert belegen:

Damit kann ich meine Klasse Portfolio bauen und testen, ohne dass es einen echten Stockservice gibt:

Ohne das Mocken müsste ich Objekte vom Interface erstellen und dort eine Logik abbilden. Das kann ich mir sparen.
Kleiner NebenEffekt: Fehler beim Starten
Wir wollten ein neues Projekt laden und hatten folgenden Fehler beim Starten:
release Version 5 not supported. Language Level is invalid or missing in pom.xml

Wir haben die pom.xml mehrfach geändert und sogar IntelliJ neu gestartet. Nichts hat geholfen. Erst als wir auf den blauen Link geklickt haben, der auch "nur" auf die 11 gezeigt hat, hat es funktioniert:
Weiterführende Doku:
Annotation + Assert
-----------------------
- https://www.w3schools.blog/junit-tutorial
Parametrisierte Tests:
- https://www.baeldung.com/parameterized-tests-junit-5
——————
Disclaimer
Alles was ich mitschrieb und verstanden habe ist ohne Gewähr.
Besten Dank an unseren
Trainer: Hans-Joachim Blanke blanke@4point.de

In den nächsten Tagen und Wochen geht’s weiter, so: stay tuned!
Gruß, Achim Mertens