
031: Recycling-Heroes - Unit Testing (Configuration API)
Nachdem wir die Configuration API fertiggestellt haben, schauen wir uns einmal das Thema Unit Tests an und wie wir unsere API automatisch testen können. Damit sparen wir uns später den Aufwand für manuelle Tests.
Inhaltsverzeichnis
Einleitung
In der letzten Episode haben wir unser API für die Konfiguration angelegt und freigegeben. Damit wir nun eine saubere und stabile API für die Zukunft haben, werden wir in dieser Folge die Unit Tests schreiben und verschiedene Szenarien mit der API testen. Dafür verwenden wir im ersten Schritt die Konfigurationen, die wir beim letzten Mal angelegt haben, um unsere Werte und Methoden zu testen.
Vorbereitungen
Wechseln wir dazu in unsere Klasse, die die eigentliche Logik implementiert. Als Erstes müssen wir den Test Code in das richtige Include verschieben, hier war uns beim letzten Mal ein Fehler unterlaufen. Grundsätzlich hat aber der Code auch an dieser Stelle funktioniert. Ist die Klasse aktiviert, dann aktivieren wir noch in den Einstellungen die Code Minings, diese findest du im Bereich der Unit Tests. Damit können wir unsere Unit Tests schneller ausführen, da wir keine Shortcuts benötigen, sondern das direkt im Code machen können. Über den "Run" können wir dann direkt unseren vorhandenen Test starten.
Um nun bequem unsere Unit Tests schreiben zu können, passen wir noch den Editor an und setzen Fokus auf unsere aktuelle Klasse. Per Doppelklick auf den aktuellen Tab maximieren wir das UI, um so mehr Platz für die Entwicklung zu haben. Über das Menü können wir nun unter "Window -> Editor -> Toggle Split Editor" den Split Editor aktivieren und Code, sowie Unit Test, nebeneinander entwickeln.
Unit Test
Beginnen wir nun mit dem Schreiben des Unit Tests. Dazu lagern wir unsere Erzeugung der "Class Under Test" oder kurz CUT, in die Setup Methode aus und legen ein Klassenattribut an. Über Rename können wir das Attribut umbenennen und machen klar, was wir eigentlich testen. Da wir für jeden Test mit einer neuen Instanz arbeiten wollen, legen wir die Setup Methode an. Weitere Informationen zu den Phasen, findest du im Blog, Link in den Kommentaren.
Definieren wir nun unseren ersten Unit Test und prüfen, ob die Price API aktiv ist, so wie wir bereits bei den Testdaten gesehen haben. Dazu verwenden wir die IS_ACTIVE Methode, die das Kennzeichen auf Datenbankebene abgleicht. Dabei verwenden wir die Methode, um gegen ABAP_TRUE zu prüfen. Erkennt der ABAP Cleaner die Prüfung, baut er meist direkt auf die passende Vergleichsmethode um. Führen wir nun den Test einmal aus und prüfen, ob unsere Daten so weit korrekt sind. Der Unit Test ist auch Grün, unsere Methode wird durchlaufen und liefert das korrekte Ergebnis.
Legen wir einen zweiten Test an und prüfen die Default Capacity. Über die Methode GET_VALUE erhalten wir unseren Wert aus dem Customizing und vergleichen diesen gegen den Vergleichswert in unserem Test. In diesem Fall passen wir den Wert auf 150 an, um ein falsches Ergebnis zu provozieren und zu prüfen, ob der Test einen Fehler erzeugt. Bei der Ausführung des Tests erscheint nun eine rote Fehlermeldung und der Test zeigt einen Fehler. Im Trace auf der rechten Seite siehst du auch den Vergleich, der schiefgelaufen ist. Unsere Logik erwartet die 150 als Wert, das Customizing liefert aber 120.
Wir korrigieren die Werte und legen noch zwei weitere Tests an. Dabei können wir einen Test direkt kopieren, wo wir uns anschauen, ob die Location API aktiv geschaltet ist. Bis auf die Anpassung der Konstanten, können wir die anderen Werte so übernehmen. Bei der letzten Methode wollen wir eine Range von der API erhalten und implementieren dazu die GET_VALUES Methode. Hier haben wir etwas mehr Aufwand den Vergleichswert zu definieren, da wir hier auch erst eine Range anlegen müssen.
Über den "Run" Button an der Testklasse, können wir alle Methoden ausführen und erhalten ein grünes Ergebnis für die 4 definierten Testmethoden.
Coverage
Für viele ist die Abdeckung oder auch Coverage ein wichtiger Punkt. Er zeigt uns, wie viel unseres Codings wir mit automatischen Tests abdecken. Daher führen wir einmal über das Menü den Unit Test mit Abdeckung aus. Im Popup wählen wir dazu Coverage aus und führen den Unit Test aus. Im ersten Schritt wird unser Code farblich markiert, dort sehen wir, welche Bestandteile durch den Unit Test bereits ausgeführt wurden und welcher Code damit automatisch getestet wird.
Im View für die Coverage erhalten wir auch Kennzahlen. Auf oberster Ebene sehen wir die Kennzahl für die Abdeckung der gesamten Klasse, dort können wir uns auch die einzelnen Methoden anschauen und sehen, dass wir nur ca. 60% der Methode GET_VALUES testen. Über den Button oben, kannst du die Markierungen im Code wieder löschen.
Abhängigkeiten
Aktuell haben wir eine große Abhängigkeit in unseren Tests. Passen wir nun das Customizing einmal an, da wir die Software weiterentwickeln oder zum Beispiel auch ein anderer Kollege etwas testen möchte. Dazu verändern wir ein paar Werte in der Konfiguration, die nun unserem neuen Teststand entsprechen. Führen wir nun unsere definierten Unit Tests aus, dann funktionieren aktuell 3 von 4 Methoden nicht mehr. Damit helfen uns die Unit Tests nur bedingt, da wir später sehr oft Anpassungen machen und das Customizing abgleichen müssen.
Test Double Framework
Dazu verwenden wir das Test Double Framework aus dem SAP Standard. Das Framework hilft uns während des Unit Tests, indem wir nicht mehr auf die echten Daten gehen, sondern auf spezifische Daten für den Test. Das spart uns Anpassungen des Codes und wir können unseren eigentlichen produktiven Code testen.
Dazu definieren wir uns einen Klasse-Attribut für das Environment und implementieren den Setup und den Teardown für die Klasse. Damit wird der Double nur einmal initialisiert. Im Teardown stellen wir sicher, dass nach unserem Test das Artefakt wieder gelöscht wird. Im Setup erstellen wir den Double für den Core Data Service und in diesem Fall definieren wir noch die Abhängigkeiten. Als nächsten Schritt definieren wir uns eine interne Tabelle für unsere neuen Testdaten. Dabei werden die Daten des Core Data Services durch unsere neuen Daten ausgetauscht. Definieren wir im ersten Schritt einmal die Daten, wie wir sie auch vorher vorgefunden haben, damit unsere aktuellen Tests wieder funktionieren. Am Ende übergeben wir die Daten an den Testdouble und aktivieren den Double.
Führen wir den Test nun aus, erhalten wir einen Fehler. Aktuell kann der Double nicht richtig erzeugt werden. Die Lösung dazu findest du in der Beschreibung der CREATE Methode. Wir müssen die DEPENDENCY_LIST nicht befüllen für den Unit Test. Räumen wir also die Methode noch einmal auf und starten den Unit Test. Nun werden wieder alle Methoden durchlaufen und wir erhalten ein grünes Ergebnis. Führen wir das Ganze mit Abdeckungsmessung durch, dann erhalten wir das gleiche Ergebnis wie zu Beginn.
Testdaten
Da wir nun die Testabdeckung erhöhen wollen, benötigen wir neue Testdaten mit anderen Werten. Meist können wir das nicht über ein Set an Daten abdecken und legen uns dazu eine neue Methode an, um weitere Testdaten für gleiche Schlüssel zu erzeugen. Damit können wir in jedem Test sagen, mit welchem Testdatenset wir testen wollen. In der neuen Methode kopieren wir uns die Datenerzeugung aus dem CLASS_SETUP und rufen im Environment die Methode CLEAR_DOUBLES. Damit löschen wir die Daten aus dem Double und können unsere neuen Testdaten hinzufügen.
Da Unit Tests nicht immer in der gleichen Reihenfolge ausgeführt werden, müssen wir sicherstellen, dass die richtigen Daten zur Verfügung stehen. Daher legen wir noch eine weitere Methode für die Datenerzeugung an und lagern die Daten aus. In unseren aktuellen Tests übernehmen wir noch das Datenset 1.
Nun können wir weitere Testmethoden für unser Datenset 2 anlegen. Wir möchten einen Testfall für eine deaktivierte Funktion haben, wo ein bis-Wert befüllt ist und eine Range mit einem Between, um die letzten Testfälle der Methode GET_VALUES zu überprüfen. Nachdem wir nun das zweite Datenset definiert haben und die Price API auf deaktiviert stehen sollte, führen wir den Unit Test dafür aus. Das Ergebnis ist Grün, unsere zusätzlichen Daten werden im Test berücksichtigt. Implementieren wir nun die weiteren Testfälle, die meist ähnlich zu den ersten Fällen sind, meist nur mit anderen Werten und Ergebnissen.
Erweiterung
Führen wir nun alle Tests aus, dann schlägt unser Test für den HIGH_VALUE fehl. In der Methode verwenden wir die GET_VALUE Methode und würden erwarten, dass der HIGH Value berücksichtigt wird, vor allem wenn kein LOW Value definiert ist. Dies ist aktuell nicht der Fall. In der Abdeckung sehen wir auch, dass die Logik nicht durchlaufen wurde, die diesen Sonderfall abdecken soll. In der Methode GET_VALUE wird direkt das Datenbankergebnis verwendet und hier finden wir keine Sonderlogik für den HIGH Value. Als Korrektur verwenden wir hier die Methode GET_VALUES und lesen die erste Zeile aus und geben den LOW-Wert zurück.
Zusammenfassung
Da unsere Klasse nun mit ausreichend Unit Tests ausgestattet ist, können wir die Anpassung durchführen und über die Tests validieren, dass unsere Funktionalität weiterhin wie gewünscht arbeitet. Alle Testmethoden sind grün und wir haben eine Abdeckung von 100% in jeder Methode erreicht. Du solltest aber beachten, dass eine Abdeckung von 100% nicht immer Sinn ergibt und manchmal nur schwer erreichbar ist. Hier sollte nach bestem Wissen und Gewissen getestet werden.
Damit sind wir am Ende der Folge. Danke fürs Zuschauen und bis zum nächsten Mal.
YouTube
Video