Что такое WireMock и для чего он используется в тестировании?

Ответ

WireMock — это инструмент для мокирования (стабирования) HTTP-сервисов. Он запускает легковесный сервер, который имитирует поведение реального внешнего API (REST, SOAP), что необходимо для изоляции тестируемого сервиса от нестабильных или платных зависимостей.

Основные сценарии использования:

  1. Интеграционное тестирование: Проверка взаимодействия вашего приложения с внешним API.
  2. Тестирование негативных сценариев: Имитация таймаутов, ошибок 5xx, невалидных ответов.
  3. Разработка без зависимостей: Возможность писать и тестировать клиентский код, когда внешний сервис ещё не готов.

Пример настройки мока в JUnit-тесте:

@SpringBootTest
@AutoConfigureWireMock(port = 8089)
public class PaymentServiceTest {

    @Test
    public void whenExternalApiReturnsSuccess_thenPaymentIsProcessed() {
        // 1. Стабируем (мокаем) ответ внешнего API
        stubFor(post(urlEqualTo("/api/v1/charge"))
                .willReturn(aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "application/json")
                        .withBody("{ "transactionId": "12345" }")));

        // 2. Вызываем наш сервис, который обратится к WireMock
        PaymentResult result = paymentService.process("order_1");

        // 3. Проверяем, что наш сервис корректно обработал ответ
        assertThat(result.isSuccess()).isTrue();
        assertThat(result.getTransactionId()).isEqualTo("12345");

        // 4. (Опционально) Верифицируем, что запрос был отправлен с правильными данными
        verify(postRequestedFor(urlEqualTo("/api/v1/charge"))
                .withRequestBody(containing("order_1")));
    }
}

Преимущества:

  • Гибкость: Полный контроль над заголовками, телом, статусом и задержкой ответа.
  • Запись и воспроизведение: Можно записать трафик к реальному API и использовать его в тестах.
  • Независимость от состояния: Моки изолированы и не зависят от данных во внешней системе.

Ответ 18+ 🔞

Да ты посмотри, что за зверь такой — WireMock! Это ж, блядь, не сервер, а какая-то мартышлюшка-обманщица для твоих HTTP-запросов. Суть проще пареной репы: запускаешь себе локально эту штуку, а она притворяется каким-нибудь важным внешним API, который тебе на самом деле нахуй не сдался в данный момент. Нужно, чтобы твоё приложение думало, будто общается с настоящим сервисом, а на деле это просто эта заглушка, блядь, отвечает чем тебе вздумается.

Зачем это, спрашивается, надо? Да элементарно!

  1. Тесты, которые не сдохнут из-за чужого косяка. Хочешь проверить, как твой код ладит с внешним миром, но этот «внешний мир» вечно то лёг, то сдох, то деньги за запросы требует? Подсовываешь ему вместо реального API — WireMock, и спи спокойно. Твои интеграционные тесты теперь в изоляции, как в скафандре.
  2. Устроить ад внешнему сервису. Ну, в тестах, конечно. Захочешь проверить, как твоё детище поведёт себя, если тот сервис начнёт отвечать ошибками 500, тормозить как черепаха или присылать откровенную дичь вместо JSON? WireMock это всё сымитирует с лёгкостью необычайной. «Хочешь таймаут? Получи таймаут, сука!»
  3. Разрабатывай, пока другие ковыряются. Внешний API ещё только в проекте, а тебе уже надо клиент под него пилить? Да хуй с ним, с проектом! Поднимаешь WireMock, описываешь, как должен вести себя будущий сервис, и работаешь дальше. Красота, ёпта!

Вот смотри, как это в коде выглядит, на простом примере:

@SpringBootTest
@AutoConfigureWireMock(port = 8089)
public class PaymentServiceTest {

    @Test
    public void whenExternalApiReturnsSuccess_thenPaymentIsProcessed() {
        // 1. Стабируем (мокаем) ответ внешнего API
        stubFor(post(urlEqualTo("/api/v1/charge"))
                .willReturn(aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "application/json")
                        .withBody("{ "transactionId": "12345" }")));

        // 2. Вызываем наш сервис, который обратится к WireMock
        PaymentResult result = paymentService.process("order_1");

        // 3. Проверяем, что наш сервис корректно обработал ответ
        assertThat(result.isSuccess()).isTrue();
        assertThat(result.getTransactionId()).isEqualTo("12345");

        // 4. (Опционально) Верифицируем, что запрос был отправлен с правильными данными
        verify(postRequestedFor(urlEqualTo("/api/v1/charge"))
                .withRequestBody(containing("order_1")));
    }
}

А в чём, собственно, сила, брат?

  • Власть абсолютная. Хочешь — правильный JSON отдавай, хочешь — хедеры кривые подставь, а хочешь — заставь ответ три секунды тупить. Всё в твоих руках, блядь.
  • Записал и забыл. Можно, как магнитофон, снять все разговоры с реальным продакшен-API, а потом в тестах этот запись включить. И не надо каждый раз вручную конфиги клепать.
  • Стабильность, мать её. Эти моки — они как в аквариуме. Что снаружи ни творись, у тебя в банке всегда одни и те же рыбки плавают. Никакие левые данные из продовой базы тебе тесты не похерят.