如何使用 Assert 来验证 MSTest 是否引发了异常?

发布于 2024-07-21 15:08:09 字数 111 浏览 9 评论 0原文

如何使用 Assert (或其他 Test 类)来验证使用 MSTest/Microsoft.VisualStudio.TestTools.UnitTesting 时是否引发了异常?

How do I use Assert (or other Test class) to verify that an exception has been thrown when using MSTest/Microsoft.VisualStudio.TestTools.UnitTesting?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(25

你的心境我的脸 2024-07-28 15:08:09

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

此处文档的示例:使用 Visual 进行单元测试演练工作室团队测试

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

For "Visual Studio Team Test" it appears you apply the ExpectedException attribute to the test's method.

Sample from the documentation here: A Unit Testing Walkthrough with 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");
}
在巴黎塔顶看东京樱花 2024-07-28 15:08:09

通常你的测试框架会有这个问题的答案。 (例如,请参阅此线程的注释,有很多!)但是如果您的框架不够灵活,您始终可以这样做:

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
}

如果您绝对必须catch Exception,需要重新抛出Assert.Fail()。 但实际上,这表明您不应该手写此内容; 检查您的测试框架的选项,或者看看是否可以抛出更有意义的异常来进行测试。

catch (AssertionException) { throw; }

您应该能够根据自己的喜好调整此方法 - 包括指定要捕获的异常类型。 如果您只期望某些类型,请使用以下命令完成 catch 块:

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

Usually your testing framework will have an answer for this. (See the comments for this thread for examples, there's lots!) But if your framework isn't flexible enough, you can always do this:

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

As @Jonas points out, this DOES NOT work for catching a base Exception:

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

If you absolutely must catch Exception, you need to rethrow the Assert.Fail(). But really, this is a sign you shouldn't be hand-writing this; check your test framework for options, or see if you can throw a more meaningful exception to test for.

catch (AssertionException) { throw; }

You should be able to adapt this approach to whatever you like -- including specifying what kinds of exceptions to catch. If you only expect certain types, finish the catch blocks off with:

} catch (GoodException) {
} catch (Exception) {
    // not the right kind of exception
    Assert.Fail();
}
那伤。 2024-07-28 15:08:09

如果你使用 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

if you use NUNIT, you can do something like this:

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

It is also possible to store the thrown exception in order to validate it further:

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

See: http://nunit.org/docs/2.5/exceptionAsserts.html

我最亲爱的 2024-07-28 15:08:09

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

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

My preferred method for implementing this is to write a method called Throws, and use it just like any other Assert method. Unfortunately, .NET doesn't allow you to write a static extension method, so you can't use this method as if it actually belongs to the build in Assert class; just make another called MyAssert or something similar. The class looks like this:

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

That means that your unit test looks like this:

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

Which looks and behaves much more like the rest of your unit test syntaxes.

撩发小公举 2024-07-28 15:08:09

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

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

您可以使用 Nuget 安装它:Install-Package MSTest.TestFramework

MSTest (v2) now has an Assert.ThrowsException() function which can be used like this:

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

You can install it with Nuget: Install-Package MSTest.TestFramework.

终陌 2024-07-28 15:08:09

如果您使用的是 MSTest,它最初没有 ExpectedException 属性,您可以这样做:

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

If you're using MSTest, which originally didn't have an ExpectedException attribute, you could do this:

try 
{
    SomeExceptionThrowingMethod()
    Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
    Assert.IsTrue(ex is SpecificExceptionType);
}
遇到 2024-07-28 15:08:09

请谨慎使用 ExpectedException,因为它可能会导致几个陷阱,如下所示:

链接

这里:

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

如果您需要测试异常,有一些不那么令人不悦的方法。 您可以使用 try{act/fail}catch{assert} 方法,该方法对于不直接支持除 ExpectedException 之外的异常测试的框架非常有用。

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

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

Be wary of using ExpectedException, as it can lead to several pitfalls as demonstrated here:

Link

And here:

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

If you need to test for exceptions, there are less frowned upon ways. You can use the try{act/fail}catch{assert} method, which can be useful for frameworks that don't have direct support for exception tests other than ExpectedException.

A better alternative is to use xUnit.NET, which is a very modern, forward looking, and extensible unit testing framework that has learned from all the others mistakes, and improved. One such improvement is Assert.Throws, which provides a much better syntax for asserting exceptions.

You can find xUnit.NET at github: http://xunit.github.io/

墨落成白 2024-07-28 15:08:09

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

首先,我不喜欢 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());            
}

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

很简单,不是吗;)

In a project I´m working on, we have another solution doing this.

First, I don´t like the ExpectedExceptionAttribute because it does take into consideration which method call that caused the Exception.

I do this with a helper method instead.

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

Neat, isn´t it ;)

浮华 2024-07-28 15:08:09

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

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

It is an attribute on the test method... you don't use Assert. Looks like this:

[ExpectedException(typeof(ExceptionType))]
public void YourMethod_should_throw_exception()
眼睛会笑 2024-07-28 15:08:09

您可以通过简单的一行来实现这一点。

如果您的操作 foo.bar() 是异步的:

await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());

如果 foo.bar() 不是异步的

Assert.ThrowsException<Exception>(() => foo.bar());

You can achieve this with a simple one-line.

If your operation foo.bar() is async:

await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());

If foo.bar() is not async

Assert.ThrowsException<Exception>(() => foo.bar());
动听の歌 2024-07-28 15:08:09

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

高级说明:下载程序集并从 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

You can download a package from Nuget using: PM> Install-Package MSTestExtensions that adds Assert.Throws() syntax in the style of nUnit/xUnit to MsTest.

High level instructions: download the assembly and inherit from BaseTest and you can use the Assert.Throws() syntax.

The main method for the Throws implementation looks as follows:

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

Disclosure: I put together this package.

More Info: http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html

紫瑟鸿黎 2024-07-28 15:08:09

在VS内置单元测试中,如果你只是想验证是否抛出了“任何异常”,但你不知道类型,你可以使用catch all:

[TestMethod]
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
public void ThrowExceptionTest()
{
    //...
}

In VS built-in unit testing if you simply want to verify that "any exception" is thrown, but you don't know the type, you can use a catch all:

[TestMethod]
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
public void ThrowExceptionTest()
{
    //...
}
计㈡愣 2024-07-28 15:08:09

我知道这个线程很旧并且有很多好的答案,但也许值得一提的是,本地函数可以以非常简单的方式提供帮助。

//Arrange

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

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

I know this thread is old and has many good answers but maybe worth mentioning that local function can help in a very simple way.

//Arrange

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

//Assert
Assert.Throws<Exception>(LocalFunction);
吾家有女初长成 2024-07-28 15:08:09

我不建议使用 ExpectedException 属性(因为它太受限制且容易出错)或在每个测试中编写 try/catch 块(因为它太复杂且容易出错)。 使用设计良好的断言方法——由您的测试框架提供或您自己编写。 这是我写的和使用的。

public static class ExceptionAssert
{
    private static T GetException<T>(Action action, string message="") where T : Exception
    {
        try
        {
            action();
        }
        catch (T exception)
        {
            return exception;
        }
        throw new AssertFailedException("Expected exception " + typeof(T).FullName + ", but none was propagated.  " + message);
    }

    public static void Propagates<T>(Action action) where T : Exception
    {
        Propagates<T>(action, "");
    }

    public static void Propagates<T>(Action action, string message) where T : Exception
    {
        GetException<T>(action, message);
    }

    public static void Propagates<T>(Action action, Action<T> validation) where T : Exception
    {
        Propagates(action, validation, "");
    }

    public static void Propagates<T>(Action action, Action<T> validation, string message) where T : Exception
    {
        validation(GetException<T>(action, message));
    }
}

使用示例:

    [TestMethod]
    public void Run_PropagatesWin32Exception_ForInvalidExeFile()
    {
        (test setup that might propagate Win32Exception)
        ExceptionAssert.Propagates<Win32Exception>(
            () => CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location, new string[0]));
        (more asserts or something)
    }

    [TestMethod]
    public void Run_PropagatesFileNotFoundException_ForExecutableNotFound()
    {
        (test setup that might propagate FileNotFoundException)
        ExceptionAssert.Propagates<FileNotFoundException>(
            () => CommandExecutionUtil.Run("NotThere.exe", new string[0]),
            e => StringAssert.Contains(e.Message, "NotThere.exe"));
        (more asserts or something)
    }

注意

返回异常而不是支持验证回调是一个合理的想法,只不过这样做会使该断言的调用语法与我使用的其他断言非常不同。

与其他人不同,我使用“传播”而不是“抛出”,因为我们只能测试异常是否从调用传播。 我们无法直接测试是否抛出异常。 但我想你可以将 throws 想象为:抛出而不是被抓住。

最终想法

在切换到这种方法之前,我考虑在测试仅验证异常类型时使用 ExpectedException 属性,并在需要更多验证时使用 try/catch 块。 但是,我不仅必须考虑每个测试使用哪种技术,而且随着需求的变化将代码从一种技术更改为另一种技术也不是微不足道的工作。 使用一种一致的方法可以节省脑力。

总而言之,这种方法具有以下优势:易用性、灵活性和稳健性(很难出错)。

更新

我的方法对于似乎在 2018 年发布的 mstest V2 不再有价值。 使用 Assert.ThrowsException。

除非您无法使用旧版本的 mstest。 那么,我的方法仍然适用。

I do not recommend using the ExpectedException attribute (since it's too constraining and error-prone) or to write a try/catch block in each test (since it's too complicated and error-prone). Use a well-designed assert method -- either provided by your test framework or write your own. Here's what I wrote and use.

public static class ExceptionAssert
{
    private static T GetException<T>(Action action, string message="") where T : Exception
    {
        try
        {
            action();
        }
        catch (T exception)
        {
            return exception;
        }
        throw new AssertFailedException("Expected exception " + typeof(T).FullName + ", but none was propagated.  " + message);
    }

    public static void Propagates<T>(Action action) where T : Exception
    {
        Propagates<T>(action, "");
    }

    public static void Propagates<T>(Action action, string message) where T : Exception
    {
        GetException<T>(action, message);
    }

    public static void Propagates<T>(Action action, Action<T> validation) where T : Exception
    {
        Propagates(action, validation, "");
    }

    public static void Propagates<T>(Action action, Action<T> validation, string message) where T : Exception
    {
        validation(GetException<T>(action, message));
    }
}

Example uses:

    [TestMethod]
    public void Run_PropagatesWin32Exception_ForInvalidExeFile()
    {
        (test setup that might propagate Win32Exception)
        ExceptionAssert.Propagates<Win32Exception>(
            () => CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location, new string[0]));
        (more asserts or something)
    }

    [TestMethod]
    public void Run_PropagatesFileNotFoundException_ForExecutableNotFound()
    {
        (test setup that might propagate FileNotFoundException)
        ExceptionAssert.Propagates<FileNotFoundException>(
            () => CommandExecutionUtil.Run("NotThere.exe", new string[0]),
            e => StringAssert.Contains(e.Message, "NotThere.exe"));
        (more asserts or something)
    }

NOTES

Returning the exception instead of supporting a validation callback is a reasonable idea except that doing so makes the calling syntax of this assert very different than other asserts I use.

Unlike others, I use 'propagates' instead of 'throws' since we can only test whether an exception propagates from a call. We can't test directly that an exception is thrown. But I suppose you could image throws to mean: thrown and not caught.

FINAL THOUGHT

Before switching to this sort of approach I considered using the ExpectedException attribute when a test only verified the exception type and using a try/catch block if more validation was required. But, not only would I have to think about which technique to use for each test, but changing the code from one technique to the other as needs changed was not trivial effort. Using one consistent approach saves mental effort.

So in summary, this approach sports: ease-of-use, flexibility and robustness (hard to do it wrong).

UPDATE

My approach is no longer valuable with mstest V2 which seems to have come out in 2018 or something. Use Assert.ThrowsException.

Unless you are stuck using an old version of mstest. Then, my approach still applies.

鱼窥荷 2024-07-28 15:08:09

好吧,我会总结一下这里其他人之前所说的...无论如何,这是我根据好的答案构建的代码:)剩下要做的就是复制和使用...

/// <summary>
/// Checks to make sure that the input delegate throws a exception of type TException.
/// </summary>
/// <typeparam name="TException">The type of exception expected.</typeparam>
/// <param name="methodToExecute">The method to execute to generate the exception.</param>
public static void AssertRaises<TException>(Action methodToExecute) where TException : System.Exception
{
    try
    {
        methodToExecute();
    }
    catch (TException) {
        return;
    }  
    catch (System.Exception ex)
    {
        Assert.Fail("Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
    }
    Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");  
}

Well i'll pretty much sum up what everyone else here said before...Anyways, here's the code i built according to the good answers :) All is left to do is copy and use...

/// <summary>
/// Checks to make sure that the input delegate throws a exception of type TException.
/// </summary>
/// <typeparam name="TException">The type of exception expected.</typeparam>
/// <param name="methodToExecute">The method to execute to generate the exception.</param>
public static void AssertRaises<TException>(Action methodToExecute) where TException : System.Exception
{
    try
    {
        methodToExecute();
    }
    catch (TException) {
        return;
    }  
    catch (System.Exception ex)
    {
        Assert.Fail("Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
    }
    Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");  
}
少年亿悲伤 2024-07-28 15:08:09

上面@Richiban 提供的帮助器效果很好,只是它不处理抛出异常的情况,但不是预期的类型。 以下内容涉及:

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

namespace YourProject.Tests
{
    public static class MyAssert
    {
        /// <summary>
        /// Helper for Asserting that a function throws an exception of a particular type.
        /// </summary>
        public static void Throws<T>( Action func ) where T : Exception
        {
            Exception exceptionOther = null;
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }
            catch (Exception e) {
                exceptionOther = e;
            }

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

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

The helper provided by @Richiban above works great except it doesn't handle the situation where an exception is thrown, but not the type expected. The following addresses that:

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

namespace YourProject.Tests
{
    public static class MyAssert
    {
        /// <summary>
        /// Helper for Asserting that a function throws an exception of a particular type.
        /// </summary>
        public static void Throws<T>( Action func ) where T : Exception
        {
            Exception exceptionOther = null;
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }
            catch (Exception e) {
                exceptionOther = e;
            }

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

                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but no exception was thrown.", typeof(T))
                    );
            }
        }
    }
}
喜你已久 2024-07-28 15:08:09

由于您提到使用其他测试类,因此比 ExpectedException 属性更好的选择是使用 Shoudly< /a> 的 Should.Throw

Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });

假设我们要求客户必须有地址才能创建订单。 如果不是,CreateOrderForCustomer 方法应导致 ArgumentException。 然后我们可以这样写:

[TestMethod]
public void NullUserIdInConstructor()
{
  var customer = new Customer(name := "Justin", address := null};

  Should.Throw<ArgumentException>(() => {
    var order = CreateOrderForCustomer(customer) });
}

这比使用 ExpectedException 属性更好,因为我们正在具体说明什么应该抛出错误。 这使得我们测试中的要求更加清晰,也使得测试失败时的诊断更加容易。

请注意,还有一个用于异步方法测试的 Should.ThrowAsync

Since you mention using other test classes, a better option than the ExpectedException attribute is to use Shoudly's Should.Throw.

Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });

Let's say we have a requirement that the customer must have an address to create an order. If not, the CreateOrderForCustomer method should result in an ArgumentException. Then we could write:

[TestMethod]
public void NullUserIdInConstructor()
{
  var customer = new Customer(name := "Justin", address := null};

  Should.Throw<ArgumentException>(() => {
    var order = CreateOrderForCustomer(customer) });
}

This is better than using an ExpectedException attribute because we are being specific about what should throw the error. This makes requirements in our tests clearer and also makes diagnosis easier when the test fails.

Note there is also a Should.ThrowAsync for asynchronous method testing.

碍人泪离人颜 2024-07-28 15:08:09

作为替代方案,您可以尝试测试实际上在测试中的接下来的两行中抛出的异常。

var testDelegate = () => MyService.Method(params);
Assert.Throws<Exception>(testDelegate);

As an alternative you can try testing exceptions are in fact being thrown with the next 2 lines in your test.

var testDelegate = () => MyService.Method(params);
Assert.Throws<Exception>(testDelegate);
风吹雨成花 2024-07-28 15:08:09

有一个很棒的库,名为 NFluent ,它可以加快并简化您编写断言的方式。

编写抛出异常的断言非常简单:

    [Test]
    public void given_when_then()
    {
        Check.ThatCode(() => MethodToTest())
            .Throws<Exception>()
            .WithMessage("Process has been failed");
    }

There is an awesome library called NFluent which speeds up and eases the way you write your assertions.

It is pretty straightforward to write an assertion for throwing an exception:

    [Test]
    public void given_when_then()
    {
        Check.ThatCode(() => MethodToTest())
            .Throws<Exception>()
            .WithMessage("Process has been failed");
    }
南巷近海 2024-07-28 15:08:09

FluentAssertions 示例

为使用该库的用户添加使用 FluentAssertions 的示例。

// act
Action result = () => {
    sut.DoSomething();
};

// assert
result.Should().Throw<Exception>();

异步示例

// act
Func<Task> result = async () => {
    await sut.DoSomethingAsync();
};

// assert
await result.Should().ThrowAsync<Exception>();

FluentAssertions Examples

Adding an example using FluentAssertions for those using that library.

// act
Action result = () => {
    sut.DoSomething();
};

// assert
result.Should().Throw<Exception>();

Async Example

// act
Func<Task> result = async () => {
    await sut.DoSomethingAsync();
};

// assert
await result.Should().ThrowAsync<Exception>();
阳光①夏 2024-07-28 15:08:09

这将取决于您使用什么测试框架?

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

[ExpectedException(typeof(ArgumentException))]

This is going to depend on what test framework are you using?

In MbUnit, for example, you can specify the expected exception with an attribute to ensure that you are getting the exception you really expect.

[ExpectedException(typeof(ArgumentException))]
深白境迁sunset 2024-07-28 15:08:09

如果使用 NUnit,请尝试以下操作:

Assert.That(() =>
        {
            Your_Method_To_Test();
        }, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));

In case of using NUnit, try this:

Assert.That(() =>
        {
            Your_Method_To_Test();
        }, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));
童话里做英雄 2024-07-28 15:08:09

查看 nUnit 文档 了解以下示例:

[ExpectedException( typeof( ArgumentException ) )]

Check out nUnit Docs for examples about:

[ExpectedException( typeof( ArgumentException ) )]
半葬歌 2024-07-28 15:08:09

尽管这是一个老问题,但我想在讨论中添加一个新的想法。 我已将“安排、行动、断言”模式扩展为“预期、安排、行动、断言”。 您可以创建一个预期的异常指针,然后断言它被分配给。 这感觉比在 catch 块中执行断言更干净,而将 Act 部分主要留给一行代码来调用被测试的方法。 您也不必从代码中的多个点进行 Assert.Fail();return 。 抛出的任何其他异常都将导致测试失败,因为它不会被捕获,并且如果抛出了您期望类型的异常,但它不是您期望的异常,则针对消息或其他属性进行断言异常有助于确保您的测试不会无意中通过。

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

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);
}
软糖 2024-07-28 15:08:09

这适用于 Visual Studio Team Test(又名 MSTest
在处理数据库或http事务时。 系统应该在某个地方抛出异常,使用 Assert.ThrowExceptionAsync<>() 将捕获您的 Throw 事件。 (在这些情况下,Assert.ThrowException<>() 不会捕获异常)。

   [TestMethod]
   public void Invalid_Input_UserName_Should_Throw_Exception()
   {
       await Assert.ThrowExceptionAsync<ExpectedExceptionType>(()=> new LogonInfo(InvalidInputInUserNameFormat,"P@ssword"));
   }

This works for Visual Studio Team Test (a.k.a MSTest)
While dealing with databases or http transaction. System should throw an exception somewhere, using Assert.ThrowExceptionAsync<>() will catch the your Throw event. (In these cases, Assert.ThrowException<>() does not catch the exception).

   [TestMethod]
   public void Invalid_Input_UserName_Should_Throw_Exception()
   {
       await Assert.ThrowExceptionAsync<ExpectedExceptionType>(()=> new LogonInfo(InvalidInputInUserNameFormat,"P@ssword"));
   }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文