Единичное тестирование внутренних исключений

Я пишу некоторые модульные тесты, используя интегрированную среду Visual Studio. Мне нужно написать несколько тестовых примеров, которые передаются при создании правильного исключения. Проблема в том, что исключения, которые мне нужно проверить, - это внутренние исключения, вложенные в более общий. Есть ли какое-то простое решение или мне нужно расширить всю функциональность. В настоящее время я использую атрибут [ExpectedException], но в такой ситуации он не будет очень хорош.

Мне также интересно, что происходит, когда мы используем [ExpectedException], в то время как у нас также есть некоторая логика Assert в самом тесте. Выполняются ли оба условия (исключение выбрано и утверждение Assert оказалось действительным) или тест проходит сразу после того, как выбрано правильное исключение?

Ответ 1

Если ваш фреймворк не поддерживает пользовательский бросок, у вас обычно есть два варианта:

  1. Реализуй это сам
  2. Изменить (или расширить) структуру

Я начну со второго решения. Рассмотрите возможность использования библиотеки 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());