Einführung in WireMock

1. Übersicht

WireMock ist eine Bibliothek zum Stubben und Verspotten von Webdiensten. Es wird ein HTTP-Server erstellt, mit dem wir uns wie mit einem tatsächlichen Webdienst verbinden können.

Wenn ein WireMock-Server in Aktion ist, können wir Erwartungen einrichten, den Dienst aufrufen und dann sein Verhalten überprüfen.

2. Maven-Abhängigkeiten

Um die WireMock-Bibliothek nutzen zu können, müssen wir die folgende Abhängigkeit in das POM aufnehmen:

 com.github.tomakehurst wiremock 1.58 test 

3. Programmatisch verwalteter Server

In diesem Abschnitt wird die manuelle Konfiguration eines WireMock-Servers beschrieben. dh ohne die Unterstützung der automatischen Konfiguration von JUnit. Die Verwendung wird durch einen sehr einfachen Stub demonstriert.

3.1. Servereinrichtung

Ein WireMock-Server kann folgendermaßen instanziiert werden:

WireMockServer wireMockServer = new WireMockServer(String host, int port);

Falls keine Argumente angegeben werden, ist der Serverhost standardmäßig localhost und der Serverport 8080 .

Der Server kann dann mit zwei einfachen Methoden gestartet und gestoppt werden:

wireMockServer.start();

Und:

wireMockServer.stop();

3.2. Grundlegende Verwendung

Die WireMock-Bibliothek wird zunächst anhand einer grundlegenden Verwendung demonstriert, bei der ein Stub für eine genaue URL ohne weitere Konfiguration bereitgestellt wird. Erstellen wir eine Serverinstanz:

WireMockServer wireMockServer = new WireMockServer();

Der WireMock-Server muss ausgeführt werden, bevor der Client eine Verbindung zu ihm herstellt:

wireMockServer.start();

Der Webdienst wird dann gestoppt:

configureFor("localhost", 8080); stubFor(get(urlEqualTo("/baeldung")).willReturn(aResponse().withBody("Welcome to Baeldung!")));

In diesem Lernprogramm wird die Apache HttpClient-API verwendet, um einen Client darzustellen, der eine Verbindung zum Server herstellt:

CloseableHttpClient httpClient = HttpClients.createDefault();

Anschließend wird eine Anforderung ausgeführt und anschließend eine Antwort zurückgegeben:

HttpGet request = new HttpGet("//localhost:8080/baeldung"); HttpResponse httpResponse = httpClient.execute(request);

Wir werden die httpResponse- Variable mithilfe einer Hilfsmethode in einen String konvertieren :

String responseString = convertResponseToString(httpResponse);

Hier ist die Implementierung dieser Konvertierungshilfemethode:

private String convertResponseToString(HttpResponse response) throws IOException { InputStream responseStream = response.getEntity().getContent(); Scanner scanner = new Scanner(responseStream, "UTF-8"); String responseString = scanner.useDelimiter("\\Z").next(); scanner.close(); return responseString; }

Der folgende Code überprüft, ob der Server eine Anfrage an die erwartete URL erhalten hat und die beim Client eingehende Antwort genau die gesendete ist:

verify(getRequestedFor(urlEqualTo("/baeldung"))); assertEquals("Welcome to Baeldung!", stringResponse);

Schließlich sollte der WireMock-Server gestoppt werden, um Systemressourcen freizugeben:

wireMockServer.stop();

4. JUnit Managed Server

Im Gegensatz zu Abschnitt 3 zeigt dieser Abschnitt die Verwendung eines WireMock-Servers mithilfe der JUnit- Regel .

4.1. Servereinrichtung

Ein WireMock-Server kann mithilfe der Annotation @Rule in JUnit-Testfälle integriert werden . Auf diese Weise kann JUnit den Lebenszyklus verwalten, den Server vor jeder Testmethode starten und nach der Rückkehr der Methode stoppen.

Ähnlich wie beim programmgesteuert verwalteten Server kann ein von JUnit verwalteter WireMock-Server als Java-Objekt mit der angegebenen Portnummer erstellt werden:

@Rule public WireMockRule wireMockRule = new WireMockRule(int port);

Wenn keine Argumente angegeben werden, nimmt der Serverport den Standardwert 8080 an . Der Serverhost, standardmäßig localhost , und andere Konfigurationen können über die Optionsschnittstelle angegeben werden.

4.2. URL-Matching

Nach dem Einrichten einer WireMockRule- Instanz besteht der nächste Schritt darin, einen Stub zu konfigurieren. In diesem Unterabschnitt stellen wir einen REST-Stub für einen Service-Endpunkt mit regulären Ausdrücken bereit:

stubFor(get(urlPathMatching("/baeldung/.*")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("\"testing-library\": \"WireMock\"")));

Fahren wir mit dem Erstellen eines HTTP-Clients fort, führen eine Anforderung aus und erhalten eine Antwort:

CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet request = new HttpGet("//localhost:8080/baeldung/wiremock"); HttpResponse httpResponse = httpClient.execute(request); String stringResponse = convertHttpResponseToString(httpResponse);

Das obige Codefragment nutzt eine Konvertierungshilfemethode:

private String convertHttpResponseToString(HttpResponse httpResponse) throws IOException { InputStream inputStream = httpResponse.getEntity().getContent(); return convertInputStreamToString(inputStream); }

Dies nutzt wiederum eine andere private Methode:

private String convertInputStreamToString(InputStream inputStream) { Scanner scanner = new Scanner(inputStream, "UTF-8"); String string = scanner.useDelimiter("\\Z").next(); scanner.close(); return string; }

The stub's operations are verified by the testing code below:

verify(getRequestedFor(urlEqualTo("/baeldung/wiremock"))); assertEquals(200, httpResponse.getStatusLine().getStatusCode()); assertEquals("application/json", httpResponse.getFirstHeader("Content-Type").getValue()); assertEquals("\"testing-library\": \"WireMock\"", stringResponse);

4.3. Request Header Matching

Now we will demonstrate how to stub a REST API with the matching of headers. Let's start with the stub configuration:

stubFor(get(urlPathEqualTo("/baeldung/wiremock")) .withHeader("Accept", matching("text/.*")) .willReturn(aResponse() .withStatus(503) .withHeader("Content-Type", "text/html") .withBody("!!! Service Unavailable !!!")));

Similar to the preceding subsection, we illustrate HTTP interaction using the HttpClient API, with help of the same helper methods:

CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet request = new HttpGet("//localhost:8080/baeldung/wiremock"); request.addHeader("Accept", "text/html"); HttpResponse httpResponse = httpClient.execute(request); String stringResponse = convertHttpResponseToString(httpResponse);

The following verification and assertions confirm functions of the stub we created before:

verify(getRequestedFor(urlEqualTo("/baeldung/wiremock"))); assertEquals(503, httpResponse.getStatusLine().getStatusCode()); assertEquals("text/html", httpResponse.getFirstHeader("Content-Type").getValue()); assertEquals("!!! Service Unavailable !!!", stringResponse);

4.4. Request Body Matching

The WireMock library can also be used to stub a REST API with body matching. Here is the configuration for a stub of this kind:

stubFor(post(urlEqualTo("/baeldung/wiremock")) .withHeader("Content-Type", equalTo("application/json")) .withRequestBody(containing("\"testing-library\": \"WireMock\"")) .withRequestBody(containing("\"creator\": \"Tom Akehurst\"")) .withRequestBody(containing("\"website\": \"wiremock.org\"")) .willReturn(aResponse() .withStatus(200)));

Now, it is time to create a StringEntity object that will be used as the body of a request:

InputStream jsonInputStream = this.getClass().getClassLoader().getResourceAsStream("wiremock_intro.json"); String jsonString = convertInputStreamToString(jsonInputStream); StringEntity entity = new StringEntity(jsonString);

The code above uses one of the conversion helper methods define before, convertInputStreamToString.

Here is content of the wiremock_intro.json file on the classpath:

{ "testing-library": "WireMock", "creator": "Tom Akehurst", "website": "wiremock.org" }

HTTP requests and responses can be configured and executed as follows:

CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost request = new HttpPost("//localhost:8080/baeldung/wiremock"); request.addHeader("Content-Type", "application/json"); request.setEntity(entity); HttpResponse response = httpClient.execute(request);

This is the testing code used to validate the stub:

verify(postRequestedFor(urlEqualTo("/baeldung/wiremock")) .withHeader("Content-Type", equalTo("application/json"))); assertEquals(200, response.getStatusLine().getStatusCode());

4.5. Stub Priority

The previous subsections deal with situations where an HTTP request matches only a single stub. It would be more complicated if there is more than a match for a request. By default, the most recently added stub will take precedence in such a case. However, users are allowed to customize that behavior to take more control of WireMock stubs.

We will demonstrate operations of a WireMock server when a coming request matches two different stubs, with and without setting the priority level, at the same time. Both scenarios will use the following private helper method:

private HttpResponse generateClientAndReceiveResponseForPriorityTests() throws IOException { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet request = new HttpGet("//localhost:8080/baeldung/wiremock"); request.addHeader("Accept", "text/xml"); return httpClient.execute(request); }

Firstly, configure two stubs without consideration of the priority level:

stubFor(get(urlPathMatching("/baeldung/.*")) .willReturn(aResponse() .withStatus(200))); stubFor(get(urlPathEqualTo("/baeldung/wiremock")) .withHeader("Accept", matching("text/.*")) .willReturn(aResponse() .withStatus(503)));

Next, create an HTTP client and execute a request using the helper method described right above:

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

The following code snippet verifies that the last configured stub is applied regardless of the one defined before when a request matches both of them:

verify(getRequestedFor(urlEqualTo("/baeldung/wiremock"))); assertEquals(503, httpResponse.getStatusLine().getStatusCode());

Let's move on to stubs with priority levels being set, where a lower number represents a higher priority:

stubFor(get(urlPathMatching("/baeldung/.*")) .atPriority(1) .willReturn(aResponse() .withStatus(200))); stubFor(get(urlPathEqualTo("/baeldung/wiremock")) .atPriority(2) .withHeader("Accept", matching("text/.*")) .willReturn(aResponse() .withStatus(503)));

Creation and execution of an HTTP request:

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

The following code validates the effect of priority levels, where the first configured stub is applied instead of the last:

verify(getRequestedFor(urlEqualTo("/baeldung/wiremock"))); assertEquals(200, httpResponse.getStatusLine().getStatusCode());

5. Conclusion

This tutorial introduced WireMock and how to set up as well as configure this library for testing of REST APIs using various techniques, including matching of URL, request headers and body.

Die Implementierung aller Beispiele und Codefragmente finden Sie in einem GitHub-Projekt.