Моки внешних HTTP-сервисов: WireMock.Net, HttpMessageHandler

↑ 5.7 Тестирование интеграций · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Как тестировать код, вызывающий внешние API, без реальных вызовов и нестабильности.

Объяснение

СпособЧто этоКогда
Подмена HttpMessageHandlerfake-обработчик, возвращающий заготовленный ответunit-тесты клиента, без сети
WireMock.Netнастоящий HTTP-сервер-заглушка со сценариямиинтеграционные тесты, проверка сериализации, заголовков, таймаутов
Контрактный тест (Pact)проверка соглашения между потребителем и поставщикомзащита от расхождения контрактов
Sandbox провайдератестовая среда внешней системыручные/e2e проверки
// WireMock.Net
using var server = WireMockServer.Start();
server.Given(Request.Create().WithPath("/v2/charges").UsingPost().WithHeader("Idempotency-Key", "*"))
      .RespondWith(Response.Create().WithStatusCode(201).WithBodyAsJson(new { id = "ch_1", status = "succeeded" }));
 
var gateway = new AcmePaymentGateway(new HttpClient { BaseAddress = new Uri(server.Url!) });
var result = await gateway.ChargeAsync(charge, default);
Assert.Equal(PaymentStatus.Succeeded, result.Status);

Сценарии сбоев (главная ценность моков):

server.Given(Request.Create().WithPath("/v2/charges").UsingPost())
      .InScenario("retry").WillSetStateTo("second").RespondWith(Response.Create().WithStatusCode(503));
server.Given(Request.Create().WithPath("/v2/charges").UsingPost())
      .InScenario("retry").WhenStateIs("second").RespondWith(Response.Create().WithStatusCode(201).WithBodyAsJson(ok));
// задержка: Response.Create().WithDelay(TimeSpan.FromSeconds(5)) — проверить таймаут

Fake handler:

class StubHandler(HttpResponseMessage response) : HttpMessageHandler
{
    public HttpRequestMessage? Last;
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage r, CancellationToken ct) { Last = r; return Task.FromResult(response); }
}

Нюансы и подводные камни

  • Мок отражает ваши предположения: при изменении настоящего API тест остаётся зелёным. Дополняйте контрактными тестами и проверкой на sandbox.
  • Проверяйте не только успех: 4xx/5xx, таймауты, некорректный JSON, лишние поля.
  • Не мокайте HttpClient через Mock<HttpClient>: подменяйте handler.
  • В WebApplicationFactory внешний адрес подставляйте через конфигурацию на URL WireMock.
  • Порты WireMock выбирайте динамически для параллельных тестов.

Практика

  1. Напишите тесты клиента платёжного API на WireMock: успех, 503→успех, таймаут, 400.
  2. Проверьте, что клиент отправляет Idempotency-Key и корректный JSON.
  3. Запишите ответы реального API как заготовки (record/playback).

Вопросы с ответами

Связанные темы