WireMock для Java: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

WireMock для Java: Полное руководство по HTTP-мок-серверу

1. Что это и зачем нужна

WireMock — это мощная библиотека для Java (и других языков через standalone-сервер), которая позволяет создавать HTTP-мок-серверы для тестирования. По сути, это эмулятор реального HTTP-сервиса (API, REST, SOAP), который вы можете настроить так, чтобы он возвращал заранее определённые ответы на определённые запросы. Это незаменимый инструмент в арсенале разработчика и тестировщика, работающего с микросервисной архитектурой или любыми системами, которые зависят от внешних HTTP-сервисов.

Зачем это нужно? В современной разработке приложения редко существуют изолированно. Они общаются с базами данных, платёжными шлюзами, сервисами авторизации, погодными API и десятками других внешних систем. Тестирование таких интеграций "вживую" — это головная боль: внешний сервис может быть недоступен, медленным, возвращать неожиданные данные или, что ещё хуже, брать с вас деньги за каждый запрос. WireMock решает эту проблему, позволяя:

  • Тестировать в изоляции: Вы тестируете только свой код, не завися от доступности и стабильности внешних сервисов.
  • Симулировать редкие сценарии: Легко заставить мок возвращать ошибки 500, таймауты, пустые ответы или специфические заголовки — то, что сложно воспроизвести с реальным сервисом.
  • Ускорять тесты: Мок-сервер отвечает за миллисекунды, в то время как реальный сервис может отвечать секунды.
  • Параллелизовать разработку: Пока backend-команда ещё не реализовала API, frontend-команда уже может писать и тестировать свой код, используя WireMock.

WireMock работает как полноценный HTTP-сервер (на базе Jetty), который запускается внутри вашего теста или как отдельный процесс. Он перехватывает HTTP-запросы, сравнивает их с настроенными правилами (stubs) и возвращает соответствующий ответ. Библиотека поддерживает запись и воспроизведение (record/playback), проверку состояния (verify), работу с шаблонами (Handlebars), симуляцию задержек и многое другое.

2. Установка

WireMock для Java распространяется через Maven Central. Добавьте следующую зависимость в ваш pom.xml (для Maven) или build.gradle (для Gradle).

Maven (pom.xml):

<dependency>
    <groupId>org.wiremock</groupId>
    <artifactId>wiremock</artifactId>
    <version>3.9.1</version> <!-- Проверьте актуальную версию на Maven Central -->
    <scope>test</scope>
</dependency>

Gradle (build.gradle):

testImplementation 'org.wiremock:wiremock:3.9.1'

Важно: Начиная с версии 3.x, WireMock требует Java 11 или выше. Если вы используете Java 8, вам нужно будет использовать версию 2.x (например, 2.35.1), но настоятельно рекомендуется перейти на Java 11+.

3. Быстрый старт — минимальный рабочий пример

Давайте создадим простейший тест, который запускает WireMock, настраивает заглушку для GET-запроса к /api/hello и проверяет, что клиент получает ожидаемый ответ.

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class QuickStartTest {

    @Test
    public void testSimpleStub() throws Exception {
        // 1. Создаём и запускаем WireMock-сервер на порту 8089
        WireMockServer wireMockServer = new WireMockServer(8089);
        wireMockServer.start();

        // 2. Настраиваем заглушку (stub)
        //    Если придёт GET-запрос на /api/hello, вернуть статус 200 и тело "Hello, World!"
        configureFor("localhost", 8089);
        stubFor(get(urlEqualTo("/api/hello"))
                .willReturn(aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "text/plain")
                        .withBody("Hello, World!")));

        // 3. Создаём HTTP-клиент и отправляем запрос
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://localhost:8089/api/hello"))
                .GET()
                .build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        // 4. Проверяем результат
        assertEquals(200, response.statusCode());
        assertEquals("Hello, World!", response.body());

        // 5. Останавливаем сервер
        wireMockServer.stop();
    }
}

Этот пример демонстрирует основную идею: Stub (правило) -> Request (запрос) -> Response (ответ).

4. Основные методы и функции

Рассмотрим ключевые методы WireMock, которые покрывают 90% задач. Все примеры предполагают, что сервер запущен и конфигурация выполнена через configureFor().

4.1. stubFor(MappingBuilder mappingBuilder)

Сигнатура: public static void stubFor(MappingBuilder mappingBuilder)

Что делает: Регистрирует новое правило (stub) в WireMock. Это основной метод для настройки поведения мок-сервера.

stubFor(get(urlEqualTo("/users"))
        .willReturn(aResponse()
                .withStatus(200)
                .withBody("[{\"id\":1, \"name\":\"Alice\"}]")));

4.2. get(String url) / post(String url) / put(String url) / delete(String url)

Сигнатура: public static MappingBuilder get(String url) (и аналогично для других HTTP-методов)

Что делает: Создают строитель (builder) для матчинга запроса по HTTP-методу и URL. Возвращают MappingBuilder, который можно дополнительно настроить (заголовки, тело запроса и т.д.).

// POST запрос
stubFor(post(urlEqualTo("/api/items"))
        .withRequestBody(matchingJsonPath("$.name"))
        .willReturn(aResponse()
                .withStatus(201)
                .withHeader("Location", "/api/items/123")));

4.3. urlEqualTo(String url) / urlPathEqualTo(String urlPath) / urlPathMatching(String regex)

Сигнатура: public static UrlPattern urlEqualTo(String url)

Что делает: Определяют стратегию сопоставления URL. urlEqualTo требует точного совпадения (включая query-параметры). urlPathEqualTo игнорирует query-параметры. urlPathMatching использует регулярное выражение.

// Точное совпадение: /api/users?page=1
stubFor(get(urlEqualTo("/api/users?page=1"))
        .willReturn(aResponse().withStatus(200)));

// Совпадение пути: /api/users/42 (query-параметры игнорируются)
stubFor(get(urlPathEqualTo("/api/users/42"))
        .willReturn(aResponse().withStatus(200)));

// Регулярное выражение: /api/users/123, /api/users/abc
stubFor(get(urlPathMatching("/api/users/[a-z0-9]+"))
        .willReturn(aResponse().withStatus(200)));

4.4. aResponse()

Сигнатура: public static ResponseDefinitionBuilder aResponse()

Что делает: Создаёт строитель для определения ответа. Позволяет настроить статус, заголовки, тело, задержку и т.д.

stubFor(get(urlEqualTo("/error"))
        .willReturn(aResponse()
                .withStatus(500)
                .withHeader("Content-Type", "application/json")
                .withBody("{\"error\":\"Internal Server Error\"}")
                .withFixedDelay(2000))); // Задержка 2 секунды

4.5. withRequestBody(WireMock.request matching)

Сигнатура: public MappingBuilder withRequestBody(ContentPattern<?> bodyPattern)

Что делает: Добавляет условие на тело запроса. Поддерживает точное совпадение, JSON Path, XPath, регулярные выражения и т.д.

// Точное совпадение тела
stubFor(post(urlEqualTo("/api/login"))
        .withRequestBody(equalToJson("{\"username\":\"admin\", \"password\":\"secret\"}"))
        .willReturn(aResponse().withStatus(200)));

// Совпадение по JSON Path
stubFor(post(urlEqualTo("/api/order"))
        .withRequestBody(matchingJsonPath("$.items[0].productId"))
        .willReturn(aResponse().withStatus(201)));

4.6. withHeader(String key, StringValuePattern headerPattern)

Сигнатура: public MappingBuilder withHeader(String key, StringValuePattern headerPattern)

Что делает: Добавляет условие на наличие и значение HTTP-заголовка в запросе.

stubFor(get(urlEqualTo("/api/secure"))
        .withHeader("Authorization", equalTo("Bearer my-token-123"))
        .willReturn(aResponse().withStatus(200).withBody("Access granted")));

// Проверка, что заголовок присутствует (значение не важно)
stubFor(get(urlEqualTo("/api/track"))
        .withHeader("X-Request-Id", matching(".*"))
        .willReturn(aResponse().withStatus(200)));

4.7. verify(RequestPatternBuilder requestPatternBuilder)

Сигнатура: public static void verify(RequestPatternBuilder requestPatternBuilder)

Что делает: Проверяет, что определённый запрос был получен сервером. Это ключевой метод для верификации поведения клиента (например, что он отправил правильный запрос).

// После выполнения тестируемого кода...
// Проверяем, что был ровно 1 POST-запрос на /api/users
verify(postRequestedFor(urlEqualTo("/api/users")));

// Проверяем, что было 2 или более GET-запроса на /api/status
verify(2, getRequestedFor(urlEqualTo("/api/status")));

// Проверяем, что запрос содержал определённый заголовок
verify(postRequestedFor(urlEqualTo("/api/data"))
        .withHeader("Content-Type", equalTo("application/json")));

4.8. reset()

Сигнатура: public static void reset()

Что делает: Сбрасывает все зарегистрированные заглушки и историю запросов. Обычно вызывается в методе @BeforeEach (JUnit 5) или @Before (JUnit 4), чтобы каждый тест начинался с "чистого листа".

import org.junit.jupiter.api.BeforeEach;

public class MyTest {
    @BeforeEach
    public void setUp() {
        WireMock.reset(); // Очищаем все заглушки и историю перед каждым тестом
    }
    // ... тесты
}

4.9. WireMockServer(WireMockConfiguration options)

Сигнатура: public WireMockServer(WireMockConfiguration options)

Что делает: Создаёт экземпляр сервера с расширенными настройками: порт, HTTPS, файлы-заглушки, расширения и т.д.

// Создаём сервер на случайном порту с HTTPS
WireMockServer server = new WireMockServer(
    WireMockConfiguration.options()
        .dynamicPort()
        .httpsSettings(new HttpsSettings(8443))
);
server.start();

// Получить динамический порт
int port = server.port();
System.out.println("Server running on port: " + port);

4.10. saveMappings()

Сигнатура: public void saveMappings()

Что делает: Сохраняет все текущие заглушки в файл (в директорию mappings). Полезно для записи (recording) реальных API.

// После того, как были зарегистрированы заглушки
wireMockServer.saveMappings();
// Файлы будут сохранены в папку mappings/ в рабочей директории

5. Реальный практический пример

Представим, что мы разрабатываем сервис для интернет-магазина, который зависит от внешнего API доставки. Нам нужно протестировать логику расчёта стоимости доставки. Внешний API может быть недоступен или медленным,

Рекомендаций