Введение в 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.