Введение в WireMock

Введение в WireMock

1. обзор

WireMock - это библиотека для создания заглушек и имитации веб-сервисов. Он создает HTTP-сервер, к которому мы могли бы подключиться, как к реальному веб-сервису.

Когда серверWireMock работает, мы можем настроить ожидания, вызвать службу, а затем проверить ее поведение.

2. Maven Зависимости

Чтобы воспользоваться библиотекой WireMock, нам нужно включить в POM следующую зависимость:


    com.github.tomakehurst
    wiremock
    1.58
    test

3. Программно управляемый сервер

В этом разделе описывается способ ручной настройки сервера WireMock. i.e. без поддержки автоконфигурации JUnit. Использование демонстрируется очень простой заглушкой.

3.1. Настройка сервера

Сервер WireMock может быть создан следующим образом:

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

Если аргументы не указаны, хост сервера по умолчанию используетlocalhost, а порт сервера -8080.

Затем сервер может быть запущен и остановлен двумя простыми способами:

wireMockServer.start();

And:

wireMockServer.stop();

3.2. Основное использование

Библиотека WireMock будет сначала продемонстрирована при базовом использовании, где предоставляется заглушка для точного URL без какой-либо дальнейшей настройки. Создадим экземпляр сервера:

WireMockServer wireMockServer = new WireMockServer();

Сервер WireMock должен быть запущен до того, как клиент подключится к нему:

wireMockServer.start();

Веб-сервис тогда заглушается:

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

В этом руководстве используется API-интерфейс Apache HttpClient для представления клиента, подключающегося к серверу:

CloseableHttpClient httpClient = HttpClients.createDefault();

Запрос выполняется и ответ возвращается соответственно:

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

Мы преобразуем переменнуюhttpResponse вString с помощью вспомогательного метода:

String responseString = convertResponseToString(httpResponse);

Вот реализация этого вспомогательного метода преобразования:

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

Следующий код проверяет, что сервер получил запрос на ожидаемый URL-адрес, а ответ, поступающий на клиент, является именно тем, что был отправлен:

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

Наконец, сервер WireMock должен быть остановлен для освобождения системных ресурсов:

wireMockServer.stop();

4. Управляемый сервер JUnit

В отличие от раздела 3, в этом разделе показано использование сервера WireMock с помощью JUnitRule.

4.1. Настройка сервера

Сервер WireMock можно интегрировать в тестовые примеры JUnit с помощью аннотации@Rule. Это позволяет JUnit управлять жизненным циклом, запуская сервер перед каждым методом тестирования и останавливая его после возврата метода.

Подобно серверу с программным управлением, управляемый JUnit сервер WireMock может быть создан как объект Java с заданным номером порта:

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

Если аргументы не указаны, порт сервера примет значение по умолчанию8080. Хост сервера, по умолчаниюlocalhost и другие конфигурации могут быть указаны с помощью интерфейсаOptions.

4.2. Соответствие URL

После настройки экземпляраWireMockRule следующим шагом будет настройка заглушки. В этом подразделе мы предоставим заглушку REST для конечной точки службы, используя регулярное выражение:

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

Перейдем к созданию HTTP-клиента, выполнению запроса и получению ответа:

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

Приведенный выше фрагмент кода использует преимущества вспомогательного метода преобразования:

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

Это, в свою очередь, использует другой частный метод:

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

Работа заглушки подтверждается приведенным ниже тестовым кодом:

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. Запросить соответствие заголовка

Теперь мы покажем, как заглушить REST API с сопоставлением заголовков. Начнем с настройки заглушки:

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

Как и в предыдущем подразделе, мы иллюстрируем HTTP-взаимодействие с использованием HttpClient API с помощью тех же вспомогательных методов:

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

Следующая проверка и утверждения подтверждают функции заглушки, которую мы создали ранее:

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

4.4. Соответствие тела запроса

Библиотека WireMock также может использоваться для создания API-интерфейса REST с сопоставлением тела. Вот конфигурация для заглушки такого типа:

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

Теперь пора создать объектStringEntity, который будет использоваться в качестве тела запроса:

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

В приведенном выше коде используется один из определенных ранее вспомогательных методов преобразованияconvertInputStreamToString.

Вот содержимое файлаwiremock_intro.json в пути к классам:

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

HTTP-запросы и ответы могут быть настроены и выполнены следующим образом:

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

Это код тестирования, используемый для проверки заглушки:

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

4.5. Заглушка Priority

Предыдущие подразделы посвящены ситуациям, когда HTTP-запрос соответствует только одной заглушке. Было бы сложнее, если бы было больше, чем совпадение для запроса. По умолчанию последняя добавленная заглушка будет иметь приоритет в этом случае. Тем не менее, пользователям разрешено настраивать это поведение, чтобы получить больший контроль над заглушками WireMock.

Мы продемонстрируем работу сервера WireMock, когда поступающий запрос совпадает с двумя разными заглушками, с и без установки уровня приоритета одновременно. В обоих сценариях будет использоваться следующий метод частного помощника:

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

Во-первых, настройте две заглушки без учета уровня приоритета:

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

Затем создайте HTTP-клиент и выполните запрос, используя вспомогательный метод, описанный выше:

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

Следующий фрагмент кода проверяет, что последняя настроенная заглушка применяется независимо от той, которая была определена ранее, когда запрос соответствует им обоим:

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

Перейдем к заглушкам с установленными уровнями приоритета, где меньшее число означает более высокий приоритет:

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

Создание и выполнение HTTP-запроса:

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

Следующий код проверяет влияние уровней приоритета, где вместо последнего применяется первый настроенный тупик:

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

5. Заключение

В этом руководстве рассказывается о WireMock и о том, как настроить и настроить эту библиотеку для тестирования API-интерфейсов REST с использованием различных методов, включая сопоставление URL, заголовков запросов и тела.

Реализацию всех примеров и фрагментов кода можно найти вa GitHub project.