Testdatenerstellung über Remote-API

Für viele kleine, nützliche Anwendung kommt der Moment, in dem diese Anwendung jenseits des ursprünglichen Aufgabengebiets eingesetzt werden soll. Selbstverständlich ist es wünschenswert, zusätzliche Kunden zu finden, doch steigt für den Entwickler mit jedem zusätzlichen Interessenten der Wunsch, spezifische, auf den Kunden zugeschnittene Testdaten zu besitzen.

Vor einigen Monaten haben wir eine kleine, spezialisierte Web-Anwendung erstellt. Sie erfüllt ihre Aufgaben zur allgemeinen Zufriedenheit und erste Erweiterungen sind mittlerweile projektiert. In der Zwischenzeit hat auch die Vermarktung dieser Anwendung begonnen. Auch hier müssen nun unterschiedliche Testdatenbestände aufgebaut werden, eine normalerweise arbeitsintensive, fehlerintensive Aufgabe.

Das Problem mit den Testdaten

Seit einigen Tagen nun betreuen wir daher mehrere unterschiedliche Testinstallationen dieses Systems. Jede dieser Installationen benötigt eigene, kundenspezifische Testdaten.

Der Aufbau und die Pflege dieser Datenhaushalte erfolgte in früheren Projekten häufig unmittelbar in der relationalen Datenbank. Dazu wurden normalerweise spezielle Ladeprogramme erstellt. Diese sind dann jeweils auf das verwendete Datenbankprodukt zugeschnitten - immerhin ist das Einspielen der Daten zeitaufwändig und soll so schnell wie möglich ablaufen.

Dazu wurden unterschiedliche Dateien in einem Format produziert, das vergleichsweise weit entfernt von den in der Fachdomäne verwendeten Konzepten und den Abstraktionen der Implementierung ist. Anstatt die einzelnen Testdaten in Form fachlich prüfbarer Testszenarien zu beschreiben, wurde eine konkrete Abbildung auf “Bit-Ebene” - also in Form von Tabellen und Attributwerten - erstellt.

Typischerweise mussten dabei Teile der Anwendungslogik in einer weiteren Implementierungssprache reimplemetiert werden. Für alle künftigen Versionen der Anwendung musste daher ein zusätzlicher, nicht zu vernachlässigender Aufwand für Entwicklung und Wartung dieser Ladeprogramme einkalkuliert werden. Dieser Aufwand muss dann in weiteren Projektkalkulationen berücksichtigt werden und führt somit zu einem zusätzlichen Anstieg der Projektkosten. Oder es wurde billigend in Kauf genommen, dass die Ladeprogramme veralten und die Testbarkeit der Anwendung damit mit der Zeit - überraschend rapide - sinkt.

In vielen Projekten, die ich habe sehen können, wird aus Kostengründen oft die oben geschilderte zweite Alternative gewählt. Wird eine Anwendung aber nur wenige Wochen weiterentwickelt, ohne die Programme für die Erzeugung der Testszenarien und Ladeprogramme weiter zu pflegen, findet sich das Entwicklungsteam schon nach kurzer Zeit mit einer Anwendung konfrontiert, die überhaupt nicht mehr automatisiert getestet werden kann. Nach meiner Erfahrung reichen dabei schon zwei bis drei Entwicklungszyklen oder “Sprints”, um die Abdeckung der automatisiert durchführbaren Tests so nachhaltig zu verschlechtern, dass die Aussagekraft der Ergebnisse vollständig in Frage zu stellen ist.

Das Hobbyprojekt

Als kleines privates Projekt habe ich bereits während der Entwicklung der Anwendung eine REST Schnittstelle realisiert. Diese Technologie habe ich gewählt, da die Integration dieser Lösung - mit dem Resteasy Framework - ohne weitreichende Eingriffe in die Kernbestandteile der Anwendung möglich wurde. Tatsächlich besteht die so geschaffene Schnittstelle aus einigen Resource-Controllern, einer Collection Implementierung und einigen besonderen Serializern, die gänzlich unabhängig in einem eigenständigen Maven Module realisiert wurden.

Der notwendige Eingriff in den bestehenden Anwendungscode besteht aus einigen wenigen Annotation, die vom Jackson Framework genutzt werden, um die gewünschten JSON Dokumente zu erstellen. Alles in allem konnte diese Implementierung einer remote Schnittstelle mit geringen Aufwand und ohne zusätzliche Risiken für das Anwendungssystem erfolgen.

Ein hoher Aufwand hätte eine Umsetzung als Hobbyprojekt unmöglich gemacht. Zusätzliche Risiken waren aufgrund der Zielvorgaben für die Anwendung nicht akzeptabel.

REST Client

Im Zuge des Hobbyprojekts wurd auch ein Client Programm für die REST Schnittstelle erstellt. Primär habe ich diesen genutzt, um die Schnittstelle prüfen zu können. Es hat sich aber gezeigt, dass mit diesem Client auch während der Entwicklung Operationen ausgelöst werden konnten, für die Datenmodell und Services vorbereitet wurden, während die Bedienoberfläche erst in einem späteren Release umgesetzte wurde.

Diesen REST Client habe ich dann herangezogen, um die Testdaten für die kundenspezifischen Testinstallationen zu erzeugen. Das Vorhandensein einer Schnittstelle für die “Fernsteuerung” der Anwendung machte es möglich, bei der Entwicklung der Testszenarien auf ein Reimplementieren fachlichen Logik vollständig zu verzichten. Stattdessen wurde für die Formulierung der Testszenarien eine Art fachlicher DSL (Domain specific Language) geschaffen.

Anstatt nun wie bisher die Daten der Testszenarien zu beschreiben, werden nun in dieser “Java DSL” die Einzelschritte beschrieben, die letztlich zur gewünschten Datenkostellation führen. Im Falle einer Szenarios für eine Urlaubsanfrage in einem HR-System würde dieser Testdatensatz wie folgt niedergeschrieben:

VacationRequest.submit(Employee.get("Ralf Stranzenbach"), "17.12.2012", "04.01.2013")
               .managerComment("Ist geklärt, wer deine Vertretung sein wird?")
               .submit("Es ist vereinbart, dass XXX mich hier vertreten wird.")
               .managerApprove("Urlaubsantrag ist genehmigt.");

VacationRequest.submit(Employee.get("Willi Wacker"), "1.1.2013", "31.12.2013")
               .managerReject("Aus Projektgründen ist eine dauerhafte Abwesenheit nicht möglich!");

Diese Testdaten können nach einer kurzen Einführung auch von fachlichen Projektmitarbeitern gelesen und verstanden werden. Für mein Programm habe ich die Szenarien aus einigen vorliegenden Excel-Datenbanken erstellt. Einige “VERKETTEN()” Makros reichen aus, um aus den bestehenden Informationen die Testszenarien zu rekonstruieren.

Jede Operation des “fluent API” ruft dabei unmittelbar die Operationen des REST API der Anwendung auf. Anpassungen und Erweiterung der Anwendung führten in unserer Anwendung nur selten zur Notwendigkeit, auch das API zu erweitern. Neu hinzukommende Attribute werden im REST API einfach mit Default-Werten befüllt, wegfallende Attribute ignoriert. Wenn aufgrund der Änderungen das API tatsöchlich erweitert werden musste, ist auch eine Erweiterung der einzelnen Szenarien sinnvoll gewesen. Daher wurde in diesem Fall einfach ein neues “Verb” in die DSL für die Testszenarien eingetragen. Dabei ist das Anlegen der Testdaten bereits als eigenständiger Test zu betrachten. Immerhin wird dem Programm in diesem Fall kein Testdatenbestand untergeschoben. Stattdessen werden Anfragen an das System in der vorgesehenen Weise verarbeitet und müssen letztendlich das erwartete Ergebnis erzeugen.

Gleichzeitig ist der erzeugte Bestand an Testdaten unabhängig von der gewählten Installationsform. Unabhängig von der Datenbank oder der genutzten Plattform (eigenständige Applikation oder Deployment in einer public-Cloud) müssen diese Testszenarien von der Anwendung in einer vergleichbaren und hoffentlich korrekten Weise verarbeitet werden. Die Erstellung unterschiedlicher Testdatensätz für die diversen unterstützten Datenbankprodukte gehört damit die der Vergangenheit an.

So war in diesem Beispiel - alles in allem - der REST Client für die Erstellung der Testdaten stabiler, als die Anwendung selbst.

Fazit

Nach meiner Erfahrung zahlen sich Aufwände, die in die Entwicklung eines REST API investiert werden, bereits während der Anwendungsentwicklung aus. Hierbei habe ich nur den Zeitaufwand betrachtet, der nun anstatt in die Entwicklung datenbankabhängiger Datengeneratoren und Ladeprogramm zu fliessen, in den REST Client und das API für die Beschreibung der Testszenarien gesteckt wird. Verglichen mit anderen, ähnlichen Projekten ist dabei in diesem Fall geringfügig weniger Zeit aufgewändet worden. Gleichzeitig aber wurden mehr, vollständigere und bessere Testdaten erstellt.

Die positiven Effekte für die Qualität des erstellten Anwendungssystems und die Verfügbarkeit kundenspezifischer Testdaten sind nur schwer zu quantifizieren. In meiner eigenen bewertenden Betrachtung wurden diese daher nicht mit berücksichtigt.

Auf einen Punkt möchte ich aber dennoch hier an dieser Stelle hinweisen, denn dieser kann kaum überbewertet werden: mit dem hier gewählten Verfahren kann programmatisch auf einfachste Weise die Anzahl der Testszenarien vervielfacht werden. So können schon in einer frühen Entwicklungsphase die erstellten Programmteile auch einer realistischen (oder besser noch unrealistisch hohen) Last überprüft werden. Mögliche Performance-Probleme werden rechtzeitig erkannt und können adressiert werden. So können die aus technischer Sicht sinnvollen Maßnahmen ergriffen werden, bevor hohe Änderungsaufwönde entstehen. Werden diese Probleme erst zu einem späteren Zeitpunkt erkannt, verbieten sich aus Kostengründen diese Maßnahmen oft und stattdessen werden günstigere “Work-Around” Lösungen implementiert.

Folgende Schlüsse ziehe ich für meine künftigen Projekte aus den hier gesammelten Erfahrungen:

  • Jede nicht-triviale Anwendung wird um ein REST-API ergänzt. Der hier investierte Entwicklungsaufwand wird durch die Erleichterungen bei der Erstellung von Testdaten aufgewogen.
  • Das REST API wird auch dann implementiert, wenn aus fachlicher Sicht keine Anforderungen dafür formuliert werden.
  • Testdaten werden über das REST-API in die Anwendung eingespielt. Auf diese Weise können die Daten programmatisch der Kunden- oder Testsituation entsprechend adaptiert werden.
  • Für die Beschreibung der Testdaten wird ein “fluid API” erstellt. Dieses kann von allen Projektbeteiligten gelesen werden und ermöglicht so die Diskussion über die Aussagekraft der Testszenarien.
  • Große Datenvolumina für Performance- und Stabilitätstests können ganz einfach durch vielfachen Aufruf der Testszenarien erzeugt werden. Auch während der Entwicklung. Schon am ersten Tag.

Weitere Artikel: