Softwareentwickler / Software Developer
Ein Hund, der sich ausruht

Testen eines REST-Clients in Java mit WireMock

Egal, ob es sich um die Abfrage einer Datenbank oder die Berechnung des Gesamtwertes eines Warenkorbs handelt, im Idealfall decken die Tests zu annähernd 100% den produzierten Code ab. Warum es sinnvoll ist, überhaupt Tests zu schreiben,  möchte ich an dieser Stelle gar nicht thematisieren. Vielmehr soll dieser Post ein Problem behandeln, welches sich mir vor einiger Zeit gestellt hat: das Testen eines REST-Clients in Java.

Die Aufgabe war, den Body einer REST-Schnittstelle in JSON-Notation mit Hilfe eines Java-Programmes auszulesen. Die API lässt dabei nur POST-Requests zu und benötigt zusätzlich eine Authentifizierung und Datums-Parameter in einem ganz bestimmten Format, die im POST-Request-Body als URL-kodierte Daten übermittelt werden. Potentielle Fehlerquellen, die durch Tests überwacht werden sollten, könnten dann zum Beispiel sein:

  • Der Pfad zur REST-API ist fehlerhaft
  • Die Authentifizierung schlägt aufgrund falscher Daten fehl
  • Das Datum wird im falschen Format übermittelt
  • Der POST-Request-Body fehlt gänzlich

Nachdem ich ein wenig kläglich gescheitert bin, das Framework „Citrus“ für meine Tests zu benutzen, bin ich auf WireMock gestoßen. Dieses Framework lässt sich als Simulator für HTTP-basierte Schnittstellen nutzen, wie es auf der Webseite beschrieben wird.

Im Folgenden möchte ich unter Nutzung des obigen Beispiels eine kleine Einführung geben, wie sich WireMock benutzen lässt.

Zuerst einmal muss die Abhängigkeit in das Build-Skript eintragen werden:

com.github.tomakehurst:wiremock:<latest_version>

Nun muss man den WireMock-Server für einen Test starten. Dies ist mit zwei Zeilen Code möglich.

@Rule
private final WireMockRule = new WireMockRule();

Als Konstruktor-Parameter kann man der WireMockRule nun optional noch ein Options-Objekt mitgeben, um zum Beispiel den zu nutzenden Port oder die Adresse, unter der der Server erreichbar sein soll, festzulegen.

@Rule
private final WireMockRule =
    new WireMockRule(
        WireMockConfiguration
            .options()
            .port(53872)
            .bindAddress("127.0.0.1"));

Jetzt hat man zwar einen Server der gestartet wird, aber es wurde noch nicht festgelegt, wie die gemockte REST-Schnittstelle zu erreichen ist. Dafür muss man einen Mock-Service erstellen.

WireMock.stubFor(post(urlEqualTo("/remoteapi/angebotscodes/")
    .withRequestBody(matching(regexRequestBody))
    .willReturn(
        aResponse()
            .withStatus(200)
            .withBody(body)));

Mit der statischen Methode stubFor(MappingBuilder mappingBuilder) wird der Mock-Service erzeugt. Da die REST API mit einem POST-Request abgefragt wird, folgt darauf die Methode post(UrlPattern urlPattern). Als Pattern muss dort dann angegeben werden, wie die Schnittstelle zu erreichen ist. Dabei sollte die Methode urlEqualTo(String testUrl) sprechend genug sein.

Als nächstes wird dann noch konfiguriert, dass es einen Request-Body geben muss. Der Inhalt muss dabei dem angegebenen regulären Ausdruck entsprechen. Für den oben beschriebenen Fall würde das folgendermaßen aussehen.

String regexRequestBody =
    "username=.*"
        + "&password=.*"
        + "&fromDate=\\d{4}-\\d{2}-\\d{2}[A-Z]\\d{2}:\\d{2}:\\d{2}.\\d*"
        + "&toDate=\\d{4}-\\d{2}-\\d{2}[A-Z]\\d{2}:\\d{2}:\\d{2}.\\d*";

Damit wäre dann zum Beispiel folgender String als Request-Body gültig:

"username=user&passwort=123&fromDate=2017-01-01T12:00:00.000&toDate=2017-01-31T12:00:00.000"

Für diesen Use-Case wurden nun alle Konfigurationen für die Schnittstelle vorgenommen. Der letzte Schritt ist dann noch, festzulegen, was als Antwort auf die Abfrage geliefert werden soll. Die Methode willReturn() erwartet als Parameter ein Objekt der Klasse ResponseDefinitionBuilder, welches mit Hilfe von aResponse() erzeugt werden kann. Ebenso wie es für das StubMapping möglich ist, kann auch beim Erzeugen eines ResponseDefinitionBuilder-Objektes eine Fluent API benutzt werden. Es kann hierbei zum Beispiel eingestellt werden, welcher Status zurückgegeben wird ebenso wie der Inhalt des Bodys.

Wichtig zu beachten ist, dass dieser Mock-Service immer nur für den einen Test gültig ist, in dem er erzeugt wurde. Benötigt man den Service also in mehreren Tests, so sollte man den obigen Code in eine Funktion auslagern und diese dann in den Tests zu Beginn aufrufen.

Fazit

Meiner Meinung nach ist WireMock in Anwendungsfällen wie diesem ein sehr empfehlenswertes Framework. Der Einstieg und die Einarbeitung dauern nicht sehr lange und die Tests sind auch durch die Fluent-API einfach zu schreiben und verstehen. Es bedarf keiner Konfiguration in externen XML-Dateien, lediglich kann man optional an der WireMockConfiguration einige Einstellungen ändern. Das Ausführen eines einzelnen JUnit-Tests dauert mit Server-Start bei mir zwischen 5 und 6 Sekunden, darauffolgende Tests laufen aber in “normaler” Geschwindigkeit ab, da das Starten des Servers schon erledigt ist. So stellt WireMock auch kein wirkliches Performance-Problem dar.

Weitere Links

WireMock – Getting Started: http://wiremock.org/docs/getting-started/
Tutorial auf ontestautomation: http://www.ontestautomation.com/getting-started-with-wiremock/

Schreibe einen Kommentar

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

2 Gedanken zu “Testen eines REST-Clients in Java mit WireMock”