【问题标题】:How can a unit test confirm an exception has been thrown单元测试如何确认已抛出异常
【发布时间】:2011-05-25 13:23:33
【问题描述】:

我正在为一个 c# 类编写单元测试,我的一个测试应该会导致该方法在添加数据时引发异常。如何使用我的单元测试来确认异常已被抛出?

【问题讨论】:

  • 什么单元测试框架?
  • Visual Studio 自带的标准单元测试系统,我不确定它有没有具体的名字

标签: c# .net unit-testing exception


【解决方案1】:

这取决于您使用的单元测试框架。在所有情况下,您可以执行以下操作:

[Test]
public void MakeItGoBang()
{
     Foo foo = new Foo();
     try
     {
         foo.Bang();
         Assert.Fail("Expected exception");
     }
     catch (BangException)
     { 
         // Expected
     }
}

在某些框架中,您可以在测试方法中添加一个属性来表达预期的异常,或者可能有一个方法,例如:

[Test]
public void MakeItGoBang()
{
     Foo foo = new Foo();
     Assert.Throws<BangException>(() => foo.Bang());
}

这样限制范围会更好,就像将其应用于整个测试一样,即使 wrong 行抛出异常,测试也可以通过。

【讨论】:

  • 第一种方法不是很好,因为它不会在引发异常的代码路径中断言。一个好的测试总是需要断言发生了一些期望。我宁愿在 catch 块中设置一个标志,或者将异常保存在局部变量中以在最后断言。
  • @Brian:断言是隐含的,因为我们没有到达 Assert.Fail (所以 an 异常已经被抛出)并且只有正确的异常被捕获(因此不能引发另一个异常)。我对这种风格完全没有意见 - 它很清楚 IMO 的预期。不过,就紧凑性而言,我更喜欢Assert.Throws。请注意,如果您需要检查异常的任何其他详细信息,可以在 catch 块中轻松完成。
  • 我同意 Assert.Throws 是理想的。尽管破译没有达到 assert.Fail() 意味着测试通过仍然是微不足道的,但我认为考虑到答案中的方法和在捕获后保存异常和断言的方法之间的选项块更清楚地表达了测试的意图。归根结底,这是一种风格选择,或许更重要的是,它呼吁微软改进其断言功能以与其他框架竞争。
  • @Brian:这绝对是一种风格选择——我自己真的不喜欢“保存例外”的方法,但我可以看到它对那些让一切都遵循 AAA 方法的人很有吸引力。
  • 当然,即使你的框架不支持,实现Assert.Throws&lt;&gt;也很简单……
【解决方案2】:
[ExpectedException(typeof(System.Exception))]

用于 Visual Studio 单元测试框架。

MSDN:

如果抛出预期的异常,测试方法将通过。

如果抛出的异常继承自预期的异常,则测试将失败。

【讨论】:

  • 这有一个不幸的属性,如果在方法内任何地方抛出异常,它将通过 - 而不仅仅是你期望的地方。
【解决方案3】:

如果您想遵循 AAA 模式(安排、行动、断言),您可以这样做,而不管测试框架如何:

[Test]
public void MyMethod_DodgyStuffDone_ThrowsRulesException() {

    // arrange
    var myObject = CreateObject();
    Exception caughtException = null;

    // act
    try {
        myObject.DoDodgyStuff();
    }
    catch (Exception ex) {
        caughtException = ex;
    }

    // assert
    Assert.That(caughtException, Is.Not.Null);
    Assert.That(caughtException, Is.TypeOf<RulesException>());
    Assert.That(caughtException.Message, Is.EqualTo("My Error Message"));
}

【讨论】:

    【解决方案4】:

    如果您使用的是 Nunit,您可以使用标记您的测试

    [ExpectedException( "System.ArgumentException" ) )]
    

    【讨论】:

    • 我不喜欢这个答案,因为它使测试通过,即使该异常被抛出您不期望的地方。
    • @abatishchev 如果您 - 例如 - 在测试中调用三个不同的方法,此属性只是确保必须在某处引发 ArgumentException,但也许您想确保仅抛出 ArgumentException通过这三种方法之一调用。
    • 好吧,你说知道你的测试会抛出异常,并且你想测试它,对吧?
    • 不,您通常希望测试一个特定操作是否引发异常。如果有其他东西抛出异常,则测试不应该通过。我也不喜欢将异常类型指定为字符串而不是使用typeof(...)
    【解决方案5】:

    您可以使用 Verify() 方法进行单元测试,并根据返回类型比较您的异常消息。

    InterfaceMockObject.Verify(i =>i.Method(It.IsAny<>())), "Your received Exception Message");
    

    您需要在 It.IsAny 块中编写一些类名或数据类型

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-17
      相关资源
      最近更新 更多