TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.77K subscribers
Post #1861 1.35K
День 1519. #Testing
Как Тестировать Контракты HTTP API в .NET
Тесты HTTP API можно называть модульными, интеграционными или компонентными – это не так важно. Важно, чтобы тесты взаимодействовали только с интерфейсом, использующимся в производственном коде.

Если определённая часть вашей системы вызывается только через HTTP API, тест должен делать то же самое. Прямой вызов метода класса контроллера ASP.NET нарушает эту идею.

Убедитесь, что вы проверяете только то, что имеет отношение к конкретному тестовому случаю:
- Если вы ожидаете исключения, убедитесь, что тип исключения правильный, свойства имеют правильные значения, а сообщение соответствует ожиданиям.
- В сообщении об исключении, часто надо проверить только отдельные части. Утверждение WithMessage в Fluent Assertions принимает шаблон сообщения именно по этой причине.
- Если API возвращает конкретный код ошибки HTTP, проверяйте только его и игнорируйте тело.
- Если тест охватывает определённый URL, где имеет значение только определённое свойство результата, игнорируйте остальные.
Это позволяет избегать провалов тестов по несвязанным причинам.

Многие разработчики используют в тестах реальный тип (например, тип DTO) из кода проекта, чтобы сравнивать с ним десериализованный результат, полученный из HTTP API. Обычный аргумент в пользу этого: это удобнее для рефакторинга, так что, например, изменение имени свойства этого типа не нарушит теста.
Но на самом деле это должно ломать тест! Маршрут, заголовки и конкретный JSON, возвращаемый HTTP API, являются контрактом и, следовательно, должны рассматриваться как таковые.

Как быть? Есть два распространённых способа:
- Использовать необработанный JSON. Самый чистый способ, но это сложно, если надо проверить только отдельные части результата.
- Десериализовать результат в анонимный тип определённой структуры. Рассмотрим пример, используя NewtonSoft.Json и Fluent Assertions:
IHost host = GetTestClient();
var response = await host.GetAsync(…);
var body = await
response.Content.ReadAsStringAsync();

var expect = new[] {
new {
State = "Active",
Count = 1
}
}

var actual = JsonConvert
.DeserializeAnonymousType(body, expect);

actual.Should().BeEquivalentTo(expect);

Здесь мы устанавливаем ожидание (expect) с определёнными значениями, а затем используем DeserializeAnonymousType, чтобы NewtowSoft.Json попытался десериализовать JSON в анонимный объект, структура которого определяется объектом expect. BeEquivalentTo использует глубокое сравнение ожидаемого и фактического (actual) объектов.

В System.Text.Json мы можем добиться того же результата:
… 
var actual = JsonSerializer.Deserialize(
body,
expect.GetType(),
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});

actual.Should().BeEquivalentTo(expect);

Вы можете инкапсулировать большую часть логики проверки в метод BeEquivalentTo, который принимает множество настроек. Также можно использовать метод Should().BeAs(), предоставляемый библиотекой FluentAssertions.Web.

Источник: https://www.continuousimprover.com/2023/03/test-http-contracts.html
  • 👍 13
More from @netdeveloperdiary
  1. Oct 12, 2026День 2812. #Карьера #AI Как Разработчикам Оставаться Полезными, Когда Код Пишет ИИ? По мер…
  2. Oct 11, 2026День 2811. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  3. Oct 10, 2026День 2810. #ЧтоНовенького #VSCode Более Быстрый и Лёгкий C# Dev Kit Мы, разработчики, люби…
  4. Oct 9, 2026День 2809. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Окончание Начало 3.…
  5. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
  6. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
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 →