Introdução ao WireMock

Introdução ao WireMock

1. Visão geral

WireMock é uma biblioteca para serviços da web de stub e simulação. Ele constrói um servidor HTTP ao qual poderíamos nos conectar como faríamos com um serviço da Web real.

Quando um servidorWireMock está em ação, podemos definir as expectativas, chamar o serviço e verificar seu comportamento.

2. Dependências do Maven

Para poder tirar proveito da biblioteca WireMock, precisamos incluir a seguinte dependência no POM:


    com.github.tomakehurst
    wiremock
    1.58
    test

3. Servidor gerenciado programaticamente

Esta seção abordará a maneira de configurar manualmente um servidor WireMock. i.e. sem o suporte da configuração automática do JUnit. O uso é demonstrado por um esboço muito simples.

3.1. Configuração do servidor

Um servidor WireMock pode ser instanciado assim:

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

Caso nenhum argumento seja fornecido, o padrão do host do servidor élocalhoste a porta do servidor é8080.

O servidor pode ser iniciado e parado usando dois métodos simples:

wireMockServer.start();

And:

wireMockServer.stop();

3.2. Uso básico

A biblioteca do WireMock será primeiramente demonstrada por um uso básico, onde é fornecido um stub para um URL exato sem nenhuma configuração adicional. Vamos criar uma instância de servidor:

WireMockServer wireMockServer = new WireMockServer();

O servidor WireMock deve estar em execução antes que o cliente se conecte a ele:

wireMockServer.start();

O serviço da Web é stubbed:

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

Este tutorial utiliza a API Apache HttpClient para representar um cliente que se conecta ao servidor:

CloseableHttpClient httpClient = HttpClients.createDefault();

Uma solicitação é executada e uma resposta é retornada, respectivamente, posteriormente:

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

Vamos converter a variávelhttpResponse emString usando um método auxiliar:

String responseString = convertResponseToString(httpResponse);

Aqui está a implementação desse método auxiliar de conversão:

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

O código a seguir verifica se o servidor recebeu uma solicitação para a URL esperada e a resposta que chega ao cliente é exatamente o que foi enviado:

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

Por fim, o servidor WireMock deve ser parado para liberar recursos do sistema:

wireMockServer.stop();

4. Servidor gerenciado JUnit

Em contraste com a seção 3, esta seção ilustra o uso de um servidor WireMock com a ajuda de JUnitRule.

4.1. Configuração do servidor

Um servidor WireMock pode ser integrado aos casos de teste JUnit usando a anotação@Rule. Isso permite que a JUnit gerencie o ciclo de vida, iniciando o servidor antes de cada método de teste e parando-o após o retorno do método.

Semelhante ao servidor gerenciado por programação, um servidor WireMock gerenciado por JUnit pode ser criado como um objeto Java com o número da porta fornecido:

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

Se nenhum argumento for fornecido, a porta do servidor assumirá o valor padrão,8080. Host do servidor, padronizado paralocalhost, e outras configurações podem ser especificadas usando a interfaceOptions.

4.2. Correspondência de URL

Depois de configurar uma instânciaWireMockRule, a próxima etapa é configurar um stub. Nesta subseção, forneceremos um stub REST para um terminal em serviço usando expressão regular:

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

Vamos continuar criando um cliente HTTP, executando uma solicitação e recebendo uma resposta:

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

O snippet de código acima aproveita um método auxiliar de conversão:

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

Por sua vez, faz uso de outro 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;
}

As operações do stub são verificadas pelo código de teste abaixo:

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

4.3. Solicitar correspondência de cabeçalho

Agora vamos demonstrar como stub uma API REST com a correspondência de cabeçalhos. Vamos começar com a configuração do stub:

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

Semelhante à subseção anterior, ilustramos a interação HTTP usando a API HttpClient, com a ajuda dos mesmos métodos auxiliares:

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

As seguintes verificações e asserções confirmam as funções do stub que criamos antes:

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

4.4. Solicitar correspondência de corpo

A biblioteca WireMock também pode ser usada para stub uma API REST com correspondência de corpo. Aqui está a configuração para um stub desse tipo:

stubFor(post(urlEqualTo("/example/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)));

Agora é hora de criar um objetoStringEntity que será usado como corpo de uma solicitação:

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

O código acima usa um dos métodos auxiliares de conversão definidos antes,convertInputStreamToString.

Aqui está o conteúdo do arquivowiremock_intro.json no classpath:

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

Solicitações e respostas HTTP podem ser configuradas e executadas da seguinte maneira:

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

Este é o código de teste usado para validar o stub:

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

4.5. Prioridade de stub

As subseções anteriores lidam com situações em que uma solicitação HTTP corresponde apenas a um único stub. Seria mais complicado se houver mais de uma correspondência para uma solicitação. Por padrão, o stub adicionado mais recentemente terá precedência nesse caso. No entanto, os usuários podem personalizar esse comportamento para ter mais controle dos stubs do WireMock.

Demonstraremos as operações de um servidor WireMock quando uma solicitação futura corresponder a dois stubs diferentes, com e sem definir o nível de prioridade, ao mesmo tempo. Ambos os cenários usarão o seguinte método auxiliar privado:

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

Primeiramente, configure dois stubs sem considerar o nível de prioridade:

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

Em seguida, crie um cliente HTTP e execute uma solicitação usando o método auxiliar descrito acima:

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

O seguinte snippet de código verifica se o último stub configurado é aplicado, independentemente do definido anteriormente quando uma solicitação corresponde a ambos:

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

Vamos prosseguir para os stubs com níveis de prioridade sendo definidos, onde um número menor representa uma prioridade mais alta:

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

Criação e execução de uma solicitação HTTP:

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

O código a seguir valida o efeito dos níveis de prioridade, onde o primeiro stub configurado é aplicado em vez do último:

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

5. Conclusão

Este tutorial apresentou o WireMock e como instalar e configurar esta biblioteca para testar APIs REST usando várias técnicas, incluindo correspondência de URL, cabeçalhos de solicitação e corpo.

A implementação de todos os exemplos e trechos de código pode ser encontrada ema GitHub project.