Alle Beiträge, Technologien & Entwicklung
Schreibe einen Kommentar

Testen von Java-REST-Clients mit Mockito und Wiremock

Die meisten Entwickler dürften schon einmal vor dem Problem gestanden haben, einen Test für Software zu entwickeln, welcher von fremden Schnittstellen abhängig ist. Die in diesem Zusammenhang auftretenden Fragen können beispielsweise lauten:

  1. Wie kann Geschäftslogik abgeprüft werden, die Zugriff auf externe Schnittstellen beinhaltet?
  2. Wie kann der Zugriff auf REST-Schnittstellen getestet werden?

Dieser Artikel soll diese beiden Fragen beantworten. Leser, die mit Mocking und Mockito bereits vertraut sind, können den ersten Abschnitt überspringen.
Am Ende des Artikels befindet sich ein Link zu dem hier vorgestellten Beispielprojekt.

Prüfen schnittstellenabhängiger Geschäftslogik

In jeder gängigen Programmiersprache existieren Bibliotheken oder Sprachkonstrukte, die es ermöglichen, den Zugriff auf Schnittstellen zu simulieren. Hierzu kommen Techniken zum Einsatz, die unter dem Begriff Mocking zusammengefasst werden.

Im Java-Umfeld hat sich hierfür die Bibliothek Mockito etabliert. Mit dieser Bibliothek ist es möglich, Objektinstanzen beliebiger Klassen zu erstellen und den Rückgabewert von Methodenaufrufen mit vorher festgelegten Ergebniswerten zu definieren.

Angenommen es existiert die Klasse RestApiConsumer, die den Zugriff auf eine externe Schnittstelle kapselt. Wir wollen Geschäftslogik testen, die über die Methode fetchData() in RestApiConsumer Daten abruft und diese manipuliert. Natürlich wäre es hier in einer Testumgebung nicht wünschenswert, dass hierfür die produktive Schnittstelle aufgerufen wird. Für den Test wird also eine präparierte Instanz von RestApiConsumer benötigt, die kontrollierte Ergebnisdaten zurückliefert, ohne den konkreten Schnittstellenaufruf auszuführen. Nachfolgend ein kleines Beispiel für die Erstellung eines solchen Mocks von RestApiConsumer:

In der ersten Zeile der Methode wird unter Verwendung von Mockito ein Mock-Objekt für RestApiConsumer erzeugt. Hierbei handelt es sich um eine zur Laufzeit erzeugte Instanz der Klasse, deren Methoden zunächst nicht ausimplementiert sind. In der zweiten Zeile werden Daten definiert, die bei einem Testaufruf zurückgegeben werden sollen. Die dritte Zeile definiert schlussendlich, dass bei dem Aufruf von consumer.fetchData(5) der Rückgabewert  data sein soll. Die Methode when wird hierbei von Mockito bereitgestellt und erlaubt es, konkrete Methodenaufrufe mit Rückgabewerten zu setzen. Aktuell ist also nur der Methodenaufruf fetchData mit dem Argument 5 definiert.

Mit Mockito ist es also möglich, konkrete Schnittstellenimplementierungen durch Mock-Objekte zu ersetzen, die kontrollierte Daten zurückliefern. Dieser Ansatz ermöglicht es, die von der Schnittstelle abhängigen Codeabschnitte zu testen. Als Frage bleibt in diesem Abschnitt offen, wie der Zugriff auf die Schnittstelle selbst geprüft werden kann. Der nächste Abschnitt wird eine Möglichkeit vorstellen, dieses Problem zu lösen.

Prüfen des Zugriffs auf die Schnittstelle

Dieser Abschnitt behandelt das Testen des Zugriffs auf die Schnittstelle selbst. Nehmen wir an, dass bereits eine REST-Schnittstellendefiniton vorliegt, der Schnittstellenanbieter diese aber noch nicht implementiert hat. Natürlich möchte man sich hier nicht in seinem Entwicklungsprozess unterbrechen lassen sowie den Code für den Schnittstellenzugriff bereits entwickeln und griffbereit haben, sobald die Schnittstelle freigegeben wird.

Bei REST-Schnittstellen steht mit Wiremock eine umfangreiche Bibliothek zur Verfügung, die für solche Testszenarien entwickelt wurde. Hierbei stellt Wiremock einen eigenen kleinen HTTP-Server bereit, der auf bestimmte Anfragen mit vordefinierten Antworten reagiert. Mit Hilfe von Wiremock kann hier schnell und einfach eine spezifikationsgemäße Schnittstelle simuliert werden.

Angenommen die Schnittstelle liefert JSON und ist folgendermaßen aufgebaut:

Mit folgendem Codefragment kann nun diese Schnittstelle simuliert werden:

Mit serviceMock wird eine Instanzvariable definiert, die einen Testserver bereitstellt. Der Testserver hört auf HTTP und HTTPS-Anfragen. Für HTTPS-Anfragen wird ein Zertifikat benötigt, welches sich einer Keystore-Datei im Ressourcen-Verzeichnis befindet. Außerdem kann der WireMock-Server mit Hilfe von GZipTransformer seine Antworten komprimieren. Dies ist zum Testen von Schnittstellen nützlich, in denen eine komprimierte Antwort vorgeschrieben ist. HTTPS und GZip sind nicht Bestandteil dieses Artikels, sind aber im Beispielprojekt zu finden. Die Methode stubService bereitet vor einem Test den Mock-Server auf eine GET-Anfrage unter der Adresse /request-data/5 vor. Sie muss immer vor einem Test aufgerufen werden, da die Konfiguration des Mock-Servers vor jedem Test zurückgesetzt wird. Wenn spezielle Testfälle eine andere Konfiguration benötigen, kann so der Testserver auch im Test selbst konfiguriert werden.

Wenn der Server eine GET-Anfrage an die entsprechende Adresse erhält, so sendet dieser durch die Definitionen in stubService() Folgendes als Antwort:

Die HTTP-Header stammen direkt aus dem Code, der Nachrichteninhalt wird aus der Datei sample-response.json aus dem src/test/resources-Verzeichnis gelesen.

Der eigentliche Test kann dann wie folgt formuliert werden:

Zuerst wird der Mock-Server konfiguriert. Als nächstes wird eine Instanz erstellt, die als API-Endpoint den lokalen Mock-Server in der HTTP-Variante verwendet. Als nächstes werden Daten mit der id 5 angefragt. Hierbei stellt die Implementierung von RestApiConsumer unter Verwendung der JAX-RS-Client-API eine Anfrage an die oben erwähne Adresse und wandelt die empfangene JSON-Nachricht in ein Java-Objekt der Klasse Data um.
Anschließend wird mit assertThat überprüft, ob das empfangene Objekt dem entspricht, was wir als Testersteller erwarten.
Zuletzt wird noch überprüft, ob die Anfrage an den Mock-Server unseren Erwartungen entsprochen hat, also in diesem konkreten Fall eine GET-Anfrage an die Adresse /request-data/5. An dieser Stelle können auch weitere Anfrageparameter, wie Header-Felder, überprüft werden.

Fazit

Dieser Artikel sollte Ihnen einen kurzen Einblick in die Welt des Testens von REST-Schnittstellen mithilfe von Wiremock verschafft haben. Dabei setzt Wiremock logisch dort an, wo Mockito aufhört. Mit Mockito ist es nur aufwendig möglich, einen Schnittstellentest durchzuführen. Für HTTP-basierte Schnittstellen ist Wiremock eine willkommene, erweiterbare und mächtige Alternative. Im Beispielprojekt finden Sie auch Beispiele für REST-Zugriffe auf Services, die HTTPS und GZip verwenden.

Downloads / Links

Hinterlassen Sie eine Antwort

Ihre E-Mail-Adresse wird nicht veröffentlicht.