【问题标题】:Unit testing for inner exceptions内部异常的单元测试
【发布时间】:2015-03-24 23:18:03
【问题描述】:

我正在使用 Visual Studio 的集成框架编写一些单元测试。我需要编写一些在引发适当异常时通过的测试用例。问题是我需要测试的异常是嵌套在更一般的异常中的内部异常。是否有一些简单的解决方案或者我需要扩展整个功能。我目前正在使用 [ExpectedException] 属性,但在这种情况下它不会有什么好处。

我也很好奇当我们使用 [ExpectedException] 时会发生什么,而我们在测试本身中也有一些 Assert 逻辑。是对条件进行评估(抛出异常并且 Assert 语句被证明是有效的)还是在抛出正确的异常后立即通过测试?

【问题讨论】:

  • 不要使用ExpectedException属性(测试可以通过,因为某些排列阶段抛出了相同的异常而不是行为阶段);使用Assert.Throws<YourException>
  • VS 的框架中没有“Throws”方法。在大多数情况下,我使用自定义异常,因此应该使属性的使用足够准确。

标签: c# entity-framework unit-testing vs-unit-testing-framework


【解决方案1】:

不是一个完整的解决方案,但在 NUnit 中,你可以做这样的事情:

 var ex = Assert.Throws<Exception>(() => thing.ThatThrows());
 Assert.That(ex.InnerException, Is.TypeOf<BadException>() );

也许你可以在你的测试框架中?

【讨论】:

  • 就像我在对问题的评论中所说的那样,VS 的框架不支持 Throws 方法,据我所知,“捕获”异常的唯一可能解决方案是使用属性。也许我应该改变代码以抛出详细的异常而不是嵌套的变化。
【解决方案2】:

如果您的框架不支持自定义投掷,您通常有两种选择:

  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 属性替换为声明实际异常实例的自定义代码。就像我说的那样,它需要更多的工作,但可以解决问题。

【讨论】:

  • 是的,应该就是这样。我只是想避免扩展功能或使用外部库,但似乎这是目前唯一可能的解决方案。谢谢。
【解决方案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;
    }
}

您还可以将其扩展为检查异常消息等。您只需要在验证方法中添加您自己的逻辑。

【讨论】:

    【解决方案4】:

    对于单元测试,我目前使用FluentAssertions。自从我学会了它,我就不想以任何其他方式断言东西了。

    要断言异常,请查看documentation的这一位

    特别是这部分

    Action act = () => subject.Foo2("Hello");
    
    act.ShouldThrow<InvalidOperationException>()
         .WithInnerException<ArgumentException>()
         .WithInnerMessage("whatever")
    

    【讨论】:

      【解决方案5】:

      FluentAssertions 真的很有帮助。

      我使用它实现了我的解决方案,如下所示。这是从AggregateException检查我的自定义异常ProcessFailureException

      Func<Task> func = async () => await item.ProcessAsync(context, message);
      func.Should().Throw<AggregateException>().WithInnerException<ProcessFailureException>();
      

      【讨论】:

        【解决方案6】:

        只需使用GetAwaiter()GetResult() 来检查内部异常:

        Assert.Throws<InnerException>(() => thing.GetAwaiter().GetResult());
        

        例如

        Assert.Throws<CommunicationException>(() => thing.GetAwaiter().GetResult());
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-02-21
          • 2011-06-21
          • 1970-01-01
          • 1970-01-01
          • 2011-08-11
          • 2013-10-10
          相关资源
          最近更新 更多