Тестируем 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