TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #2500 2.49K
День 2069. #ЗаметкиНаПолях
Тестируем HttpClient, Вызывающий API

API можно тестировать с помощью WebApplicationFactory, как было рассмотрено здесь. Но что, если нужно «имитировать» вызовы HttpClient внутри тестов? Например, у вас нет контроля над API, но нужно протестировать реакцию приложения на получение определённого ответа от API.

Допустим, у нас такой минимальный API:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddHttpClient<GreeterService>(
o => o.BaseAddress =
new Uri("https://www.website.com"));

var app = builder.Build();
app.MapGet("/", async (GreeterService svc)
=> await svc.GetSomethingAsync());

app.Run();

public sealed class GreeterService
{
private readonly HttpClient _client;

public GreeterService(HttpClient client)
=> _client = client;

public async Task<string> GetSomethingAsync()
=> await _client.GetStringAsync("/api");
}


Мы используем HttpClient внутри GreeterService. Как нам теперь протестировать этот класс? Мы бы не хотели делать настоящие HTTP-запросы в тестах (по крайней мере, к внешним системам), поскольку они делают тест нестабильным и ненадёжным. Кроме того, сложно проверить, как наш сервис будет обрабатывать отказы API.

HttpMessageHandler / DelegatingHandler
DelegatingHandler — это «промежуточное ПО» для HttpClient. Каждый раз, когда вызывается SendAsync, этот обработчик будет вызываться. Обычно его используют для ведения журнала, аутентификации и т. д. Но мы также можем использовать его в тестовых сценариях!

Обработчик для нашего теста будет выглядеть следующим образом:
public class FakeDataHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage>
SendAsync(
HttpRequestMessage request,
CancellationToken ct)
{
if (request.RequestUri is
{ AbsoluteUri: "https://www.website.com/api" })
{
return Task.FromResult(
new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("Hello World"),
});
}

return base.SendAsync(request, ct);
}
}

Теперь используем FakeDataHandler:
public ApiTests(
WebApplicationFactory<Program> factory)
{
_factory = factory.WithWebHostBuilder(s =>
s.ConfigureTestServices(sc =>
{
sc.AddTransient<FakeDataHandler>();
sc.ConfigureHttpClientDefaults(d =>
d.AddHttpMessageHandler<FakeDataHandler>());
}));
}

И следующий тест пройдёт:
[Fact]
public async Task ShouldGreet()
{
using var client = _factory.CreateClient();

var response = await client.GetStringAsync("/api");

Assert.NotNull(response);
Assert.Equal("Hello World", response);
}

Так мы можем настраивать ответы, также проверять, как наш сервис реагирует на неудачные ответы, эмулировав их. Кроме того, при этом вы не делаете ненужных запросов к API.

Источник: https://steven-giesel.com/blogPost/47e4a351-0f1b-4bb5-a3d5-0ce4d7c1fa61/how-to-test-httpclient-inside-api-tests
  • 👍 17
More from @netdeveloperdiary
  1. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  2. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  3. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  4. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  5. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  6. Oct 3, 2026Post #3360
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →