TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #1003 1.3K
День восемьсот семнадцатый. #ЗаметкиНаПолях
Тестирование Исключений с Помощью xUnit и Action
Когда вы пишете модульные тесты для метода, рекомендуется тестировать различные условия отказа («печальные пути») в дополнение к проверке того, что всё работает («счастливого пути»). В частности, если у вас есть метод, который может генерировать исключения, особенно если это пользовательские типы исключений, специфичные для вашего домена, вы должны быть уверены, что тестируете это поведение. Другой распространённый источник исключений - это защитные предложения, которые используются для поддержания чистоты вашего метода и обеспечения того, чтобы входные данные соответствовали ожиданиям метода.

В отличие от NUnit и MSTest, которые в основном используют атрибуты для ожидаемых исключений, xUnit предоставляет метод Assert.Throws<T>, который используется для проверки ожидаемых исключений (в NUnit3 также появился аналогичный метод):
Customer customer = null;
Assert.Throws<NullReferenceException>(
() => customer.LastName);

Кроме того, метод возвращает пойманное исключение, которое можно обработать:
var ex = Assert.Throws<NameRequiredException>(
() => customer.UpdateName("", ""));
Assert.Equal("Имя не задано", ex.Message);

Arrange, Act, Assert и Исключения
Многие тесты используют шаблон тестирования AAA (Arrange, Act, Assert – Настройка, Действие, Утверждение). Проблема с предыдущим примером в том, что перехват исключения метода относится как к блоку Act, так и к блоку Assert. Кроме того, эта строка получается слишком длинной.

В этом случае для строгого следования шаблону AAA делегат для Assert.Throws можно выделить в отдельную переменную типа Action:
[Fact]
public void ThrowsExceptionGivenInvalidName {
// Arrange
var customer = new Customer();
// Act
Action act = () => customer.UpdateName("", "");
// Assert
var ex = Assert.Throws<NameRequiredException>(act);
Assert.Equal("Имя не задано", ex.Message);
}

Теперь блок Действие явно содержит нужную операцию, а строка Assert.Throws гораздо короче и понятнее.

Заметьте также, что имя теста явно говорит о том, что тест выбрасывает исключение и при каких условиях.

FluentAssertions
Об этой библиотеке подробно было рассказано во вчерашнем видео. Если вы используете её, то код утверждения будет выглядеть примерно так:
[Fact]
public void ThrowsExceptionGivenInvalidName {
// …

// Assert
action.Should()
.Throw<NameRequiredException>()
.WithMessage("Имя не задано");
}


Источник: https://ardalis.com/testing-exceptions-with-xunit-and-actions/
More from @netdeveloperdiary
  1. Sep 30, 2026Post #3356
  2. Sep 29, 2026Фото 3 (с) Анатолий Кулаков
  3. Sep 29, 2026День 2799. Конференция DotNext 2026. Часть 1 25 и 26 сентября в Москве прошла очередная ко…
  4. Sep 28, 2026День 2798. #Оффтоп Утиная Типизация в C# с Помощью Перехватчиков. Часть 2 Некоторое время…
  5. Sep 27, 2026День 2797. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Окончание Начало Продол…
  6. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .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 →