我如何使用Assert(或其他测试类)来验证在使用MSTest/Microsoft.VisualStudio.TestTools.UnitTesting时抛出了异常?


当前回答

Even though this is an old question, I would like to add a new thought to the discussion. I have extended the Arrange, Act, Assert pattern to be Expected, Arrange, Act, Assert. You can make an expected exception pointer, then assert it was assigned to. This feels cleaner than doing your Asserts in a catch block, leaving your Act section mostly just for the one line of code to call the method under test. You also don't have to Assert.Fail(); or return from multiple points in the code. Any other exception thrown will cause the test to fail, because it won't be caught, and if an exception of your expected type is thrown, but the it wasn't the one you were expecting, Asserting against the message or other properties of the exception help make sure your test won't pass inadvertently.

[TestMethod]
public void Bar_InvalidDependency_ThrowsInvalidOperationException()
{
    // Expectations
    InvalidOperationException expectedException = null;
    string expectedExceptionMessage = "Bar did something invalid.";

    // Arrange
    IDependency dependency = DependencyMocks.Create();
    Foo foo = new Foo(dependency);

    // Act
    try
    {
        foo.Bar();
    }
    catch (InvalidOperationException ex)
    {
        expectedException = ex;
    }

    // Assert
    Assert.IsNotNull(expectedException);
    Assert.AreEqual(expectedExceptionMessage, expectedException.Message);
}

其他回答

这取决于您使用的测试框架?

例如,在MbUnit中,您可以用一个属性指定预期的异常,以确保您得到的是真正预期的异常。

[ExpectedException(typeof(ArgumentException))]

这是测试方法的一个属性…你不使用Assert。看起来是这样的:

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

我最喜欢的实现方法是编写一个名为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());
}

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

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

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

我用一个helper方法来代替它。

Test

[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());            
}

HelperMethod

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;
    }

很整洁,不是吗?)

我知道这个帖子很老了,有很多很好的答案,但值得一提的是,局部函数可以以一种非常简单的方式提供帮助。

//Arrange

//Act
void LocalFunction() => mr.ActualMethod(params);

//Assert
Assert.Throws<Exception>(LocalFunction);