Перейти к основному содержимому

Введение в WireMock

· 6 мин. чтения

1. Обзор

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

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

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

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

<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>1.58</version>
<scope>test</scope>
</dependency>

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

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

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

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

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

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

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

wireMockServer.start();

А также:

wireMockServer.stop();

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

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

WireMockServer wireMockServer = new WireMockServer();

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

wireMockServer.start();

Затем веб-служба заглушается:

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

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

CloseableHttpClient httpClient = HttpClients.createDefault();

Выполняется запрос и возвращается ответ, соответственно, после этого:

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

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

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("/foreach")));
assertEquals("Welcome to ForEach!", stringResponse);

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

wireMockServer.stop();

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

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

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

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

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

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

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

4.2. Сопоставление URL

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

stubFor(get(urlPathMatching("/foreach/.*"))
.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/foreach/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("/foreach/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("/foreach/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/foreach/wiremock");
request.addHeader("Accept", "text/html");
HttpResponse httpResponse = httpClient.execute(request);
String stringResponse = convertHttpResponseToString(httpResponse);

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

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

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

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

stubFor(post(urlEqualTo("/foreach/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/foreach/wiremock");
request.addHeader("Content-Type", "application/json");
request.setEntity(entity);
HttpResponse response = httpClient.execute(request);

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

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

4.5. Приоритет заглушки

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

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

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

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

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

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

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

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

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

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

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

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

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

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

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

5. Вывод

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

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