1. Información general
WireMock es una biblioteca para copiar y burlarse de servicios web. Construye un servidor HTTP al que podríamos conectarnos como lo haríamos con un servicio web real.
Cuando un servidor WireMock está en acción, podemos configurar expectativas, llamar al servicio y luego verificar su comportamiento.
2. Dependencias de Maven
Para poder aprovechar la biblioteca WireMock, necesitamos incluir la siguiente dependencia en el POM:
com.github.tomakehurst wiremock 1.58 test
3. Servidor administrado mediante programación
Esta sección cubrirá la forma de configurar manualmente un servidor WireMock. es decir, sin el soporte de la configuración automática de JUnit. El uso se demuestra mediante un código auxiliar muy simple.
3.1. Configuración del servidor
Se puede crear una instancia de un servidor WireMock de esta manera:
WireMockServer wireMockServer = new WireMockServer(String host, int port);
En caso de que no se proporcionen argumentos, el host del servidor predeterminado es localhost y el puerto del servidor es 8080 .
Luego, el servidor puede iniciarse y detenerse mediante dos métodos simples:
wireMockServer.start();
Y:
wireMockServer.stop();
3.2. Uso básico
La biblioteca WireMock se demostrará en primer lugar mediante un uso básico, donde se proporciona un código auxiliar para una URL exacta sin ninguna configuración adicional. Creemos una instancia de servidor:
WireMockServer wireMockServer = new WireMockServer();
El servidor WireMock debe estar ejecutándose antes de que el cliente se conecte a él:
wireMockServer.start();
Luego, el servicio web se corta:
configureFor("localhost", 8080); stubFor(get(urlEqualTo("/baeldung")).willReturn(aResponse().withBody("Welcome to Baeldung!")));
Este tutorial hace uso de la API Apache HttpClient para representar a un cliente que se conecta al servidor:
CloseableHttpClient httpClient = HttpClients.createDefault();
Se ejecuta una solicitud y se devuelve una respuesta, respectivamente, después:
HttpGet request = new HttpGet("//localhost:8080/baeldung"); HttpResponse httpResponse = httpClient.execute(request);
Convertiremos la variable httpResponse en un String usando un método auxiliar:
String responseString = convertResponseToString(httpResponse);
Aquí está la implementación de ese método de ayuda de conversión:
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; }
El siguiente código verifica que el servidor haya recibido una solicitud a la URL esperada y la respuesta que llega al cliente es exactamente la que se envió:
verify(getRequestedFor(urlEqualTo("/baeldung"))); assertEquals("Welcome to Baeldung!", stringResponse);
Finalmente, el servidor WireMock debe detenerse para liberar recursos del sistema:
wireMockServer.stop();
4. Servidor administrado JUnit
A diferencia de la sección 3, esta sección ilustra el uso de un servidor WireMock con la ayuda de JUnit Rule .
4.1. Configuración del servidor
Un servidor WireMock se puede integrar en los casos de prueba de JUnit utilizando la anotación @Rule . Esto permite que JUnit administre el ciclo de vida, iniciando el servidor antes de cada método de prueba y deteniéndolo después de que regrese el método.
De forma similar al servidor administrado mediante programación, un servidor WireMock administrado por JUnit se puede crear como un objeto Java con el número de puerto dado:
@Rule public WireMockRule wireMockRule = new WireMockRule(int port);
Si no se proporcionan argumentos, el puerto del servidor tomará el valor predeterminado, 8080 . El host del servidor, el predeterminado es localhost y otras configuraciones pueden especificarse utilizando la interfaz de Opciones .
4.2. Coincidencia de URL
Después de configurar una instancia de WireMockRule , el siguiente paso es configurar un stub. En esta subsección, proporcionaremos un código auxiliar REST para un punto final de servicio usando una expresión regular:
stubFor(get(urlPathMatching("/baeldung/.*")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("\"testing-library\": \"WireMock\"")));
Pasemos a crear un cliente HTTP, ejecutar una solicitud y recibir una respuesta:
CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet request = new HttpGet("//localhost:8080/baeldung/wiremock"); HttpResponse httpResponse = httpClient.execute(request); String stringResponse = convertHttpResponseToString(httpResponse);
El fragmento de código anterior aprovecha un método de ayuda de conversión:
private String convertHttpResponseToString(HttpResponse httpResponse) throws IOException { InputStream inputStream = httpResponse.getEntity().getContent(); return convertInputStreamToString(inputStream); }
Esto a su vez hace uso de otro método privado:
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.
La implementación de todos los ejemplos y fragmentos de código se puede encontrar en un proyecto de GitHub.