如何使用 Assert 验证是否已引发异常?

如何使用 Assert(或其他 Test 类?)来验证是否已引发异常?

答案

对于 “Visual Studio Team Test”,您似乎将 ExpectedException 属性应用于该测试的方法。

这里的文档样本: 使用 Visual Studio Team Test 进行单元测试的演练

[TestMethod]
[ExpectedException(typeof(ArgumentException),
    "A userId of null was inappropriately allowed.")]
public void NullUserIdInConstructor()
{
   LogonInfo logonInfo = new LogonInfo(null, "P@ss0word");
}

通常,您的测试框架会对此提供答案。但是,如果它不够灵活,可以随时执行以下操作:

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // If it gets to this line, no exception was thrown
} catch (GoodException) { }

正如 @Jonas 指出的那样,这对于捕获基本异常无效:

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // raises AssertionException
} catch (Exception) {
    // Catches the assertion exception, and the test passes
}

如果绝对必须捕获 Exception,则需要重新抛出 Assert.Fail()。但是,实际上,这是一个不应该手写的迹象;检查测试框架中的选项,或者查看是否可以抛出更有意义的异常以进行测试。

catch (AssertionException) { throw; }

您应该能够使此方法适应您喜欢的任何方式 - 包括指定要捕获的异常类型。如果只希望使用某些类型,请使用以下方法结束catch

} catch (GoodException) {
} catch (Exception) {
    // not the right kind of exception
    Assert.Fail();
}

实现此目的的首选方法是编写一个称为 Throws 的方法,并像其他任何 Assert 方法一样使用它。不幸的是,.NET 不允许您编写静态扩展方法,因此您无法像使用该方法实际上属于 Assert 类中的内部版本一样使用此方法。只需创建另一个名为 MyAssert 或类似名称的文件即可。该类如下所示:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        public static void Throws<T>( Action func ) where T : Exception
        {
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }

            if ( !exceptionThrown )
            {
                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but not thrown", typeof(T))
                    );
            }
        }
    }
}

这意味着您的单元测试如下所示:

[TestMethod()]
public void ExceptionTest()
{
    String testStr = null;
    MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper());
}

它的外观和行为更像其余的单元测试语法。

如果您使用的是 MSTest(最初没有ExpectedException属性),则可以执行以下操作:

try 
{
    SomeExceptionThrowingMethod()
    Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
    Assert.IsTrue(ex is SpecificExceptionType);
}

如果您使用 NUNIT,则可以执行以下操作:

Assert.Throws<ExpectedException>(() => methodToTest());


还可以存储抛出的异常以便进一步验证它:

ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);

请参阅: http : //nunit.org/docs/2.5/exceptionAsserts.html

谨慎使用 ExpectedException,因为它可能导致以下陷阱:

http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx

和这里:

http://xunit.github.io/docs/comparisons.html

如果您需要测试例外情况,那么对方法的皱眉就更少了。您可以使用 try {act / fail} catch {assert} 方法,该方法对于不直接支持除 ExpectedException 之外的异常测试的框架很有用。

更好的替代方法是使用 xUnit.NET,它是一个非常现代,具有前瞻性和可扩展性的单元测试框架,该框架从其他所有错误中吸取了教训并进行了改进。 Assert.Throws 是这样的改进之一,它为断言提供了更好的语法。

您可以在 github 上找到 xUnit.NET: http://xunit.github.io/

在一个正在进行的项目中,我们有另一个解决方案。

首先,我不喜欢 ExpectedExceptionAttribute,因为它确实考虑了导致异常的方法调用。

我用一种辅助方法来代替。

测试

[TestMethod]
public void AccountRepository_ThrowsExceptionIfFileisCorrupt()
{
     var file = File.Create("Accounts.bin");
     file.WriteByte(1);
     file.Close();

     IAccountRepository repo = new FileAccountRepository();
     TestHelpers.AssertThrows<SerializationException>(()=>repo.GetAll());            
}

辅助方法

public static TException AssertThrows<TException>(Action action) where TException : Exception
    {
        try
        {
            action();
        }
        catch (TException ex)
        {
            return ex;
        }
        Assert.Fail("Expected exception was not thrown");

        return null;
    }

整洁,不是吗?)

MSTest(v2)现在具有一个 Assert.ThrowsException 函数,可以像这样使用:

Assert.ThrowsException<System.FormatException>(() =>
            {
                Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
            });

您可以使用 nuget 进行Install-Package MSTest.TestFrameworkInstall-Package MSTest.TestFramework

它是测试方法的一个属性... 您不使用 Assert。看起来像这样:

[ExpectedException(typeof(ExceptionType))]
public void YourMethod_should_throw_exception()

您可以使用以下方法从 Nuget 下载软件包: PM> Install-Package MSTestExtensions ,它以 nUnit / xUnit 样式向 MsTest 添加了Assert.Throws()语法。

高级说明:下载程序集并从BaseTest继承,您可以使用Assert.Throws()语法。

Throws 实现的主要方法如下所示:

public static void Throws<T>(Action task, string expectedMessage, ExceptionMessageCompareOptions options) where T : Exception
{
    try
    {
        task();
    }
    catch (Exception ex)
    {
        AssertExceptionType<T>(ex);
        AssertExceptionMessage(ex, expectedMessage, options);
        return;
    }

    if (typeof(T).Equals(new Exception().GetType()))
    {
        Assert.Fail("Expected exception but no exception was thrown.");
    }
    else
    {
        Assert.Fail(string.Format("Expected exception of type {0} but no exception was thrown.", typeof(T)));
    }
}

披露:我整理了这个包装。

更多信息: http : //www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html