Единичное тестирование внутренних исключений
Я пишу некоторые модульные тесты, используя интегрированную среду Visual Studio. Мне нужно написать несколько тестовых примеров, которые передаются при создании правильного исключения. Проблема в том, что исключения, которые мне нужно проверить, - это внутренние исключения, вложенные в более общий. Есть ли какое-то простое решение или мне нужно расширить всю функциональность. В настоящее время я использую атрибут [ExpectedException], но в такой ситуации он не будет очень хорош.
Мне также интересно, что происходит, когда мы используем [ExpectedException], в то время как у нас также есть некоторая логика Assert в самом тесте. Выполняются ли оба условия (исключение выбрано и утверждение Assert оказалось действительным) или тест проходит сразу после того, как выбрано правильное исключение?
Ответы
Ответ 1
Если ваш фреймворк не поддерживает пользовательский бросок, у вас обычно есть два варианта:
- Реализуй это сам
- Изменить (или расширить) структуру
Я начну со второго решения. Рассмотрите возможность использования библиотеки FluentAssertions. Это позволяет вам сделать что-то вроде этого:
Action deleteUser = () => usersRepository.Delete(new User { Id = null });
deleteUser
.ShouldThrow<UserNotFoundException>()
.WithInnerException<ArgumentNullException>()
.WithInnerMessage("User Id must have value");
Вы по-прежнему будете использовать среду тестирования Visual Studio, просто у вас будет одна дополнительная библиотека для хорошо беглых утверждений.
С другой стороны, первый выбор - это немного больше работы, как это обычно бывает с решениями, раскрученными вручную:
try
{
usersRepository.Delete(new User { Id = null });
Assert.Fail("Deleting user with null id should throw");
}
catch (UserNotFoundException ue)
{
Assert.AreEqual(ue.InnerException.Message, "User Id must have value");
}
Атрибут ExpectedException
заменяется пользовательским кодом, подтверждающим фактический экземпляр исключения. Как я уже сказал, это больше работы, но делает свое дело.
Ответ 2
Не полное решение, но в Nunit вы можете делать такие вещи
var ex = Assert.Throws<Exception>(() => thing.ThatThrows());
Assert.That(ex.InnerException, Is.TypeOf<BadException>() );
Возможно, вы можете в своей тестовой среде?
Ответ 3
Это старый вопрос, но я хочу поделиться с вами своей репутацией ExpectedInnerExceptionAttribute
с вами, ребята. может быть полезно для кого-то
public class ExpectedInnerExceptionAttribute : ExpectedExceptionBaseAttribute
{
public ExpectedInnerExceptionAttribute(Type exceptionType)
{
this.ExceptionType = exceptionType;
}
public Type ExceptionType { get; private set; }
protected override void Verify(Exception ex)
{
if (ex != null && ex.InnerException != null
&& ex.InnerException.GetType() == this.ExceptionType)
{
return;
}
throw ex;
}
}
Вы также можете расширить его, чтобы проверить сообщение об исключении и т.д. вам просто нужно добавить свою собственную логику в метод Verify.
Ответ 4
Для модульного тестирования в настоящее время я использую FluentAssertions. Поскольку я узнал об этом, я никогда не хотел утверждать материал каким-либо другим способом.
Для утверждения исключений посмотрите на этот бит documentation
В частности, эта часть
Action act = () => subject.Foo2("Hello");
act.ShouldThrow<InvalidOperationException>()
.WithInnerException<ArgumentException>()
.WithInnerMessage("whatever")
Ответ 5
Просто используйте GetAwaiter()
и GetResult()
для проверки внутреннего исключения:
Assert.Throws<InnerException>(() => thing.GetAwaiter().GetResult());
например
Assert.Throws<CommunicationException>(() => thing.GetAwaiter().GetResult());