Introduzione a WireMock

1. Panoramica

WireMock è una libreria per stubbing e derisione di servizi web. Costruisce un server HTTP a cui possiamo connetterci come faremmo con un vero servizio web.

Quando un server WireMock è in azione, possiamo impostare aspettative, chiamare il servizio e quindi verificare i suoi comportamenti.

2. Dipendenze di Maven

Per poter sfruttare la libreria WireMock, dobbiamo includere la seguente dipendenza nel POM:

 com.github.tomakehurst wiremock 1.58 test 

3. Server gestito a livello di codice

Questa sezione illustrerà il modo per configurare manualmente un server WireMock. cioè senza il supporto della configurazione automatica di JUnit. L'utilizzo è dimostrato da uno stub molto semplice.

3.1. Configurazione del server

Un server WireMock può essere istanziato in questo modo:

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

Nel caso in cui non vengano forniti argomenti, l'host del server viene impostato su localhost e la porta del server su 8080 .

Il server può quindi essere avviato e arrestato utilizzando due semplici metodi:

wireMockServer.start();

E:

wireMockServer.stop();

3.2. Utilizzo di base

La libreria WireMock sarà innanzitutto dimostrata da un utilizzo di base, in cui viene fornito uno stub per un URL esatto senza ulteriori configurazioni. Creiamo un'istanza del server:

WireMockServer wireMockServer = new WireMockServer();

Il server WireMock deve essere in esecuzione prima che il client si connetta ad esso:

wireMockServer.start();

Il servizio web viene quindi stub:

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

Questo tutorial utilizza l'API Apache HttpClient per rappresentare un client che si connette al server:

CloseableHttpClient httpClient = HttpClients.createDefault();

Successivamente viene eseguita una richiesta e viene restituita una risposta:

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

Convertiremo la variabile httpResponse in una stringa utilizzando un metodo di supporto:

String responseString = convertResponseToString(httpResponse);

Ecco l'implementazione di quel metodo di supporto per la conversione:

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; }

Il codice seguente verifica che il server abbia ricevuto una richiesta all'URL previsto e che la risposta che arriva al client sia esattamente quella che è stata inviata:

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

Infine, il server WireMock dovrebbe essere arrestato per rilasciare le risorse di sistema:

wireMockServer.stop();

4. JUnit Managed Server

Contrariamente alla sezione 3, questa sezione illustra l'utilizzo di un server WireMock con l'aiuto di JUnit Rule .

4.1. Configurazione del server

Un server WireMock può essere integrato nei casi di test JUnit utilizzando l' annotazione @Rule . Ciò consente a JUnit di gestire il ciclo di vita, avviando il server prima di ogni metodo di test e interrompendolo dopo la restituzione del metodo.

Simile al server gestito a livello di programmazione, un server WireMock gestito da JUnit può essere creato come un oggetto Java con il numero di porta specificato:

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

Se non vengono forniti argomenti, la porta del server assumerà il valore predefinito, 8080 . L'host del server, l'impostazione predefinita di localhost e altre configurazioni possono essere specificate utilizzando l' interfaccia Opzioni .

4.2. Corrispondenza URL

Dopo aver configurato un'istanza WireMockRule , il passaggio successivo consiste nel configurare uno stub. In questa sottosezione, forniremo uno stub REST per un endpoint di servizio utilizzando un'espressione regolare:

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

Passiamo alla creazione di un client HTTP, all'esecuzione di una richiesta e alla ricezione di una risposta:

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

Lo snippet di codice precedente sfrutta un metodo di supporto per la conversione:

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

Questo a sua volta fa uso di un altro metodo privato:

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.

L'implementazione di tutti gli esempi e frammenti di codice può essere trovata in un progetto GitHub.