12 VIEWS

Erzeugung von Testobjekten mit dem Builder-Pattern (lesbar und flexibel)

02.10.2020

Tests prüfen und dokumentieren Quellcode. Daher sollten sie sauber und verständlich sein. Die Erzeugung von Testobjekten stört oft den Lesefluss. Insbesondere dann, wenn mehrere Varianten benötigt werden, die sich nur in wenigen Werten unterscheiden. Hier hilft das Builder-Pattern enorm.

Ausgangssituation ohne Builder

Aus Platzgründen wollen wir hier nur einen Ausschnitt der Testobjekt-Instanzierungen abbilden. Man kann sich jedoch leicht vorstellen wie dies mit mehreren Testobjekten aussieht…

  • Geschwätzig und unübersichtlich. Der Leser tut sich schwer, Initialisierung von Testobjekten von der tatsächlichen Testlogik abzugrenzen. Dies gilt vor allem dann, wenn man mehrere solcher Objekte erzeugen muss, die sich nur in wenigen Attributwerten unterscheiden.
  • Aufwändig wenn man solche Instanzierungen häufig vornehmen muss. => Man hilft sich manchmal mit Erzeuger-Methoden.
  • Bei diesen besteht jedoch die Gefahr, dass die Parameter-Reihenfolge versehentlich vertauscht wird (auch wenn moderne IDEs hier durch Hints helfen). Richtig hässlich wird es aber, wenn später weitere leicht veränderte Varianten benötigt werden… 🙁
  • Bei Erzeugung mehrere Testobjekte besteht außerdem die Gefahr, dass man beim Kopieren vergisst, die Referenz anzupassen:

 

Mit einfachem Builder

  • Fluent-API fördert die Lesbarkeit und trennt durch Einrückung Objektinitialisierung besser vom restlichen Testcode.
  • Vermeidet die ständige Wiederholung der Variablen address.
  • Verhindert versehentliches Vertauschen der Parameter-Reihenfolge. Zwar helfen hier manche IDEs durch Einblenden der Parameternamen (z.B. IntelliJ), jedoch ist dies dem Lesefluss auch nicht so richtig zuträglich.
  • der Builder kann einfach in anderen Tests wiederverwendet werden. Daher lohnt sich meist der Mehraufwand bei der Erstellung (IDE-Plugins, sowie die mächtigen Such-/Ersetzen-Funktionen moderner IDEs helfen hierbei).

Hinweis: Der Builder ist hier als eigene Klasse umgesetzt. Das entspricht nicht 100% dem echten Builder-Pattern. Es hat jedoch folgende Vorteile: Man muss die ursprüngliche Klasse aus dem Produktivcode nicht verändern (dies sollte man auch nicht tun, wenn es sich um Code handelt, der nur für Tests relevant ist). Dadurch kann der Builder spezielle Methoden erhalten, die nur für Tests bestimmt sind. Häufige Initialisierungen lassen sich so verbergen – wie wir gleich noch sehen werden.

Mit speziellem Testdaten-Builder

Zusätzlich zu den Vorteilen des einfachen Builders kommt hier Folgendes hinzu:

  • Information Hiding und Fokussierung auf die für den Test relevanten Attribute:
    Wiederkehrendes kann versteckt werden ohne Lesbarkeit einzubüßen: Die Max-Mustermann-Adresse kommt immer wieder in Tests vor. Dabei kommt es in den Tests hauptsächlich auf die weiteren Attribute an. Mit einem speziellen Testdatenbuilder lassen sich wiederkehrende Initialisierung, die nur dazu dienen, dass der Code keine Fehler wirft, in Hilfsmethoden verlagern.
    => So kann man Unwichtiges verstecken und die für den Test wichtigen Attribute hervorheben (in diesem Fall sind dies die Attribute email, verified und historic). 🙂
  • Erzeugung unterschiedlicher Repräsentationen möglich:
    Fügt man dem Builder weitere build-Methoden hinzu, lassen sich dadurch ohne großen Aufwand verschiedene Repräsentationen erzeugen. Wir haben dies zum Beispiel in Integrationstests genutzt: Eine build-Methode erzeugt JSON für die Anlage des Testdatensatzes in der NoSQL-Datenbank. Eine weitere generiert das Löschstatement um die Testdaten wieder aufzuräumen.

Der Testobjekt-Builder sieht in unserem Beispiel wie folgt aus:

Fazit

Das Builder-Pattern hilft bei der Instanzierung von Testobjekten. Es erlaubt eine Fokussierung auf die relevanten Attribute. Durch die automatische Einrückung durch die IDE kann man Testobjekt-Instanzierung und Testlogik leichter unterscheiden. Beides führt zu besserer Lesbarkeit der Tests. Der Builder lässt sich außerdem sehr leicht für weitere Tests wiederverwenden – auch dann wenn Varianten oder komplett unterschiedlicher Repräsentationen benötigt werden. Der Mehrwert ist enorm und wiegt den Mehraufwand beim Erstellen des Builders meist auf. 😎

 

👉 Meine Empfehlung ist daher, das Builder-Pattern gleich von Anfang an einzusetzen. Ein nachträglicher Umbau ist aufwändig und macht wenig Spass. Und der Builder muss zu Beginn auch gar nicht alle Attribute unterstützen. Es genügt ja, Setter-Methoden für die momentan benötigten Attribute anzubieten. Nach und nach kann der Testobjekt-Builder dann erweitert werden.

Zurück zur Übersicht

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*Pflichtfelder

*

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.