Hoverfly ist ein Tool, das speziell für die Simulation und Kontrolle von HTTP- und HTTPS-Interaktionen entwickelt wurde. Es bietet Entwicklern die Möglichkeit, Abhängigkeiten von externen Diensten während des Testens zu minimieren, indem man realistische Simulationen dieser Dienste bereitstellt. In diesem Techup gucken wir die grundlegenden Themen an und zeige auf, wie man Hoverfly in einem Java-Projekt einsetzen kann inklusive GitHub-Actions.
Simulieren von HTTP-Anfragen
Der zentrale Bestandteil der Bibliothek ist die Hoverfly-Klasse, die für die Abstraktion und Steuerung einer Hoverfly-Instanz sorgt. Hier ist ein typischer Ablauf:
|
|
Verfügbare Betriebsmodi von Hoverfly
Bevor wir uns die ein Praxisbeispiel Step by Step anschauen, möchte ich kurz erklären, welche unterschiedlichen Betriebsmodi zur Verfügung stehen um unterschiedliche Testanforderungen abzudecken und die API-Simulation optimal zu nutzen.
Simulationsmodus (Simulating)
Im Simulationsmodus verhält sich Hoverfly so, als wäre es der echte Dienst und beantwortet Anfragen entsprechend. Das ist besonders praktisch, um die Abhängigkeit von externen Diensten während des Testens zu vermeiden. Später zeigen wir noch ein konkretes Beispiel, wie man diesen Modus einrichtet.
|
|
Proxy-Modus (Spy-Modus)
Der SPY-Modus von Hoverfly ermöglicht es, HTTP-Anfragen an eine reale API weiterzuleiten, wenn keine passende Simulation für diese Anfragen vorhanden ist. Dabei fungiert Hoverfly als Proxy, der den Netzwerkverkehr überwacht und aufzeichnet. Wenn eine Anfrage ausgeführt wird, prüft Hoverfly zunächst, ob es eine Simulation für diese Anfrage gibt. Falls ja, wird die gemockte Version zurückgeliefert. Falls nein, wird die Anfrage an die echte API weitergeleitet.
|
|
[!NOTE]
Genaues Beispiel bei uns im Techup-Repo
Aufzeichnen von API-Anfragen (Capture-Modus)
Hoverfly kann auch im Capture-Modus betrieben werden, wobei der Netzwerkverkehr zu einer realen API aufgezeichnet und die erhaltenen Responses gespeichert werden können. Hierfür muss die Methode exportSimulation
definiert werden. Die gespeicherten JSONs können dann in einem weiteren Schritt sogar im Simulationsmodus genutzt werden. Mit diesem Modus lässt sich somit der gewünschte Request sehr genau nachbauen.
|
|
[!NOTE]
Genaues Beispiel bei uns im Techup-Repo
Unterschiede erkennen: Der Diff-Modus
Im Diff-Modus erkennt Hoverfly die Unterschiede zwischen einer Simulation und den tatsächlichen Anfragen und Antworten. Hoverfly erstellt dann einen Diff-Bericht, den man später überprüfen kann.
|
|
Erweiterte Funktionen von Hoverfly
Neben den grundlegenden Betriebsmodi bietet Hoverfly eine Reihe erweiterter Funktionen, die es ermöglichen, komplexere Szenarien zu simulieren und die Flexibilität der API-Tests deutlich zu erhöhen.
API-Endpunkte definieren: Nutzung der Domain Specific Language (DSL)
Hoverfly bietet eine DSL, um Anfragen und Antworten in Java statt als JSON zu definieren. Dies ermöglicht eine fluente und hierarchische Definition von Endpunkten.
|
|
Simulieren von Netzwerklatenzzeiten
Es ist möglich, Netzwerklatenzzeiten zu simulieren, entweder global für alle Anfragen oder spezifisch für bestimmte HTTP-Methoden.
|
|
Anfragen präzise matchen: Request Field Matchers
Hoverfly bietet Matchers, um komplexe Anfragen zu definieren, wie z.B. Wildcards, Regex oder JSON-Path-Matching.
|
|
Zustandsbasierte Simulationen (Stateful Simulation)
Hoverfly unterstützt zustandsbasierte Simulationen, bei denen ein Dienst unterschiedliche Antworten basierend auf dem aktuellen Zustand zurückgibt.
|
|
Verifizieren von Anfragen: Die Verification-Funktion
Mit der Verifikationsfunktion kann überprüft werden, ob bestimmte Anfragen an die externen Dienstendpunkte gestellt wurden.
|
|
Hands-On – REST-Client in Java mit Hoverfly testen
In diesem Teil werden wir Schritt für Schritt durchgehen, wie man einen REST-Client in Java implementiert mit unterschiedlichen API-Endpunkten implementiert. Dieses Beispiel verwendet den MicroProfile Rest Client und zeigt, wie man Hoverfly nutzen kann, um diese Interaktion zu simulieren und zu testen.
Installation der benötigten Abhängigkeiten
In unserem Fall haben wir eine Quarkus Applikation mit maven. Somit fügen wir einfach folgende Dependencies in unsere pom.xml ein:
|
|
Erstellen und Konfigurieren des REST-Clients
Zunächste definieren wir eine Schnittstelle mit dem Namen BnovaRestClient
. Mit der Anotation @Path
definieren wird den Base-Path für diesen Endpunkt, in diesem Fall /techhub
.
Für die Regestrierung des Clients, wird die Annotation @RegisterRestClient
verwendet. Diese Annotation ermöglicht es, die Schnittstelle später innerhalb unseres Controller zu injecten und zu verwenden.
Die erste Methode die wir anlegen ist getById
. Dabei setzen wir die @GET
Annotation, was bedeutet, dass es ein HTTP-GET
-Request ist. Die @Produces
-Annotation zeigt an, dass die Methode eine JSON-Response zurückgeben soll. Diese Methode nimmt dabei einen QueryParam id
entgegen, um das Techup-Objekt basierend auf der übergebenen ID abzurufen.
|
|
Um den REST-Client in unserer Anwendung zuverwenden, benötigen wir einen Controller, der von der Applikation aufgerufen werden kann. Der Controller wird quasi als Vermittler zwischen den Anfragen, die an unsere Anwendung gestellt werden, und dem REST-Client, den wir zuvor definiert haben genutzt.
Hier ist der Code für den TechupController
:
|
|
Der TechupController
ist ein REST-Controller, der Anfragen an den Pfad /techhub
verarbeitet, was über die Annotation @Path
definiert wird.
Über die Annotation @RestClient
wird der BnovaRestClient
in den Controller injected. Dadurch kann der Controller, den REST-Client verwenden, um externe Anfragen zu stellen.
Die Methode getTechupById
ist mit @GET
annotiert, was sie als HTTP-GET-Anfrage kennzeichnet. Der spezifische Pfad für diese Methode ist /{id}
, wobei id
ein Pfadparameter ist. Die @Produces
-Annotation gibt an, dass die Methode eine JSON-Antwort liefert.
Einrichten und Durchführen von Tests
Nachdem wir nun den REST-Client und den Controller implementiert haben, ist der nächste Schritt das Erstellen von Tests notwendig, um sicherzustellen, dass auch alles wie erwartet funktioniert.
Die Testklasse erstellen: TechupTest
Zuerst erstellen wir die Testklasse TechupTest
. Dafür benötigen wir zwei Annotation einmal @QuarkusTest
mit der die Klasse als Quarkus-Test gekennzeichnet wird, damit das Quarkus-Test-Framework die Umgebung initialisiert. Die Annotation @QuarkusTestResource(value = HoverflyResource.class)
verknüpft den Test mit der HoverflyResource
. Diese Ressource werden wir im nächsten Schritt auch als Test-Ressource anlegen und dabei Hoverfly als Simulation der API-Aufrufe nutzen.
Für alle folgenden Tests wird die Bibliothek RestAssured verwendet, um eine HTTP-GET-Anfrage an den entsprechenden Endpunkt zusenden. In dem Test testGetById
handelt es sich dabei um den Pfad /techhub/{id}
. Mit der Methode given()
wird der Pfadparameter id
auf 1
gesetzt. Anschließend wird mit when()
und get("/techhub/{id}")
die Anfrage ausgeführt.
In der anschließenden then()
-Kette wird überprüft, ob der HTTP-Statuscode der Response 200 (OK)
ist, ob der Content-Type der Antwort application/json
ist und ob die Felder der Antwort den erwarteten Werten entsprechen.
|
|
Hoverfly-Resource für Mocking anlegen
Nun müssen wir auch noch die HoverflyResource
anlegen, auf welche wir bereits in der Testklasse verwiesen haben. Diese Klasse dient als unsere Mock-Umgebung für unseren BnovaRestClient
.
Dabei implementiert die Klasse HoverflyResource
das Interface QuarkusTestResourceLifecycleManager
, welche Methoden zur Verwaltung des Lebenszyklus der Test-Ressource bereitstellt.
In der start()
-Methode wird als erstes eine Hoverfly-Instanz mit der Constanten SIMULATE
im Simulationsmodus gestartet damit wir simulieren können, wie die API-Antworten aussehen sollen. Als erster Parameter wird jedoch die Methode localConfigs()
aufgerufen, die eine Konfiguration für die lokale Ausführung von Hoverfly zurückgibt. Diese Konfiguration enthält verschiedene standard Einstellungen. Das einzige was wir hier anpassen ist die die Ziel Url, welche Hoverfly simulieren soll. Dabei haben wir die Konstante SERVICE_URL
genutzt, welche “my-hoverfly-service
” als Wert hat. Dadurch können wir sicher sein, dass nur Requests zu dieser spezifischen URL von Hoverfly abgefangen und simuliert werden. Damit dies auch später über die CLI funktioniert und auch innerhalb der Github-Actions setzten wir diesen Wert ebenfalls über die application.properties
:
|
|
Um den eigentlichen Simulationsmodus zu starten, wird die Methode .simulate()
verwendet. Als Parameter können dann die einzelnen Mocks definiert werden. Die kann man am besten mit der Domain Soecific Language machen, damit es klar strukturiert und auch lesbar ist. Hierzu muss man einfach die Methode .dsl()
nutzen.
In dem folgenden Beispiel wird spezifiziert, dass die Simulation auf GET-Anfragen an den Pfad /techhub
reagieren soll, wenn es einen Parameter id
mit dem Wert 1
gibt. Wenn dies der Fall ist soll der Inhalt aus dem JSON-File example_get_by_id.json
zurück geben werden.
|
|
Bei der Response handelt es sich um ein Techup Objekt im JSON-Format:
|
|
Hoverfly in GitHub Actions integrieren
Wollen wir beispielsweise nun unsere Applikation mittels GitHub Action builden, ist ein mvn clean install
notwendig. Dabei werden auch alle Test ausgeführt. Und auch hier muss natürlich unsere Hoverfly Mock laufen. Und hier kommt die grosse Überraschung, es ist nichts weiter zu tun als ein den Build zu starten. Hier ist meine Beispiel GitHub Action:
|
|
Sobald ich nun auf main pushe, startet die Action. Dabei kann man im Log sehen, dass diese erfolgreich ausgeführt werden:
|
|
Fazit
Hoverfly ist ein äußerst vielseitiges und leistungsstarkes Tool, das speziell für die Simulation von API-Interaktionen in Microservices-Umgebungen entwickelt wurde. Mit seinen verschiedenen Betriebsmodi bietet es eine umfassende Lösung, um Abhängigkeiten von externen Diensten während des Testens zu eliminieren, reale API-Anfragen aufzuzeichnen und zu simulieren sowie Unterschiede zwischen Simulationen und tatsächlichen Antworten zu erkennen. Dank der nahtlosen Integration in Java-Projekte und CI/CD-Pipelines ermöglicht Hoverfly eine kontinuierliche und automatisierte Überprüfung von Anwendungen, was die Zuverlässigkeit und Effizienz im Entwicklungsprozess deutlich erhöht. Entwickler, die realistische und flexible API-Simulationen benötigen, finden in Hoverfly ein passendes Werkzeug.
Auch in unterschiedlichen Kundenprojekten ist Hoverfly bereits erfolgreich integriert!
[!TIP]
Die gesamten Beispiele sind auch in unserem Techhub Repository verfügbar