Abweichungen zwischen Spezifikation und REST-API mit hikaku erkennen

Keine Kommentare

Wenn man eine REST-API mit dem Contract-first-Ansatz erstellt, verwendet man vermutlich Codegenerierung oder einen anderen Weg, um sicherzustellen, dass die Spezifikation und die Implementierung im Laufe der Zeit inhaltlich gleich bleiben. In diesem Artikel möchte ich zeigen, wie hikaku mit nur wenigen Zeilen Code helfen kann, genau das sicherzustellen.

Contract-first ohne Codegenerierung

Die bekannten Frameworks zur Erstellung von REST-APIs werben damit, dass die Erstellung von Services schnell und einfach gemacht ist. Warum nutzen wir also Codegenerierung, wenn wir uns für einen Contract-first-Ansatz entschieden haben? – Weil wir auf diese Weise sicherstellen können, dass unsere Spezifikation und Implementierung inhaltlich gleich bleiben und der Code bei Änderungen an der Spezifikation automatisch angepasst wird. Ich sehe in diesem Vorgehen aber nicht immer einen Vorteil. Für kleine Änderungen oder neue Endpunkte, die ich manuell schnell geschrieben hätte, muss ich neue Frameworks und Abhängigkeiten ins Projekt hinzufügen, die Einfluss auf meinen Build-Prozess nehmen. Wir koppeln unsere Spezifikation und die Implementierung eng miteinander. Es kommt ggf. häufig zu Problemen im Build-Prozess, der im Verlauf komplexer wurde und vielleicht sogar chaotischer. Der generierte Code ist vielleicht nicht schön, aber wir sagen uns, dass es sich ja um ein Generat handelt und wir uns deshalb keine Gedanken darüber machen sollten. Oder wir gehen einen Schritt weiter und individualisieren den generierten Code, den wir dann aber ja auch maintainen müssen. Brauchen wir das alles wirklich in unserem Projekt?

Aber wie testet man das?

Nutzen wir keine Codegenerierung, müssen wir irgendwie anders dafür sorgen, dass die strukturelle Gleichheit von Spezifikation und Code gegeben ist und bleibt. Als ich vor dieser Herausforderung stand, wollte ich keine Testaufrufe machen, die primär das Verhalten testen und implizit auch die zugrundeliegende Struktur. Alles, was ich wollte, war ein Unit Test, dem ich sagen kann, von welchem Typ meine Spezifikation ist und mit welchem Framework ich die Umsetzung gemacht habe. Der Test soll dann von allein in der Lage sein, mir zu sagen, ob beide übereinstimmen oder wo ggf. die Abweichungen sind.

Hier kommt hikaku ins Spiel. Hierbei handelt es sich um eine Library, die ich genau für diesen Fall gebaut habe. Sie ist in Kotlin geschrieben, lässt sich aber natürlich auch für andere Sprachen der JVM, wie Java, nutzen. Aufgeteilt in ein Core-Modul und ein Modul für jeden Converter, ist es für die Unterstützung verschiedener Frameworks ausgelegt.

Beispielservice

Okay, genug geredet. Schauen wir uns etwas Code an. In diesem Beispielprojekt verwenden wir OpenAPI 3 als Spezifikation.

Für die Implementierung benutzen wir Spring.

Den Testfall erstellen

Das gesamte Beispielprojekt ist auf github verfügbar.
Als erstes fügen wir die Abhängigkeiten für hikaku zu unserer build.gradle.kts hinzu:

Als nächstes erstellen wir einen JUnit Test, in dem wir den Spring-Kontext starten und uns in die Testklasse injecten.

Innerhalb unseres Tests erstellen wir uns eine Instanz des OpenApiConverter und übergeben den Pfad zu unserer openapi.yaml. Für unsere Implementierung nutzen wir den SpringConverter. Hier übergben wir den ApplicationContext, den wir uns injected haben. Beide EndpointConverter nutzen wir bei der Instanziierung der Hikaku-Klasse.
Spring erstellt standardmäßig /error-Endpunkte, die wir in unserem Test gerne ignorieren möchten. Hikaku bietet uns hier die Möglichkeit, mithilfe der Klasse HikakuConfig eine Menge von Pfaden zu definieren, die bei der Prüfung nicht herangezogen werden soll.
Und das war es dann auch schon.

Testergebnisse

Was ist mit den Testergebnissen? Ein MatchResult wird an alle registrierten Reporter geschickt, bevor der Test terminiert. Der Standardreporter schreibt einfach das Ergebnis auf System.out. Zusätzliche Reporter lassen sich hinzufügen und ebenfalls registrieren.

Wenn unser Service und unsere Spezifikation übereinstimmen, bekommen wir folgende Konsolenausgabe:

Service und Specification stimmen überein

Andernfalls wird der Test fehlschlagen und ausgeben, welche Endpunkte er erwartet hätte, aber nicht gefunden hat und welche Endpunkte er gefunden hat, obwohl sie nicht erwartet wurden.

erwartete und unerwartete Endpunkte

Am besten das Repository des Beispielprojektes klonen und einfach selbst ausprobieren. Ich würde mich freuen zu hören, wie ihr eure REST-APIs erstellt, und ob diese Library für euer Projekt hilfreich sein könnte.

Jannes Heinrich

Jannes arbeitet als Software-Engineer und IT-Consultant bei der codecentric. Sein Schwerpunkt liegt im Java-Umfeld. Er ist immer an neuen Technologien und Frameworks interessiert und daran diese sinnvoll und gewinnbringend in Projekten einzusetzen.

Kommentieren

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