如何使用 Assert 来验证 MSTest 是否引发了异常?
如何使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(25)
对于“Visual Studio Team Test”,您似乎将 ExpectedException 属性应用于测试的方法。
此处文档的示例:使用 Visual 进行单元测试演练工作室团队测试
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
通常你的测试框架会有这个问题的答案。 (例如,请参阅此线程的注释,有很多!)但是如果您的框架不够灵活,您始终可以这样做:
正如@Jonas 指出的那样,这不适用于捕获基本异常:
如果您绝对必须catch Exception,需要重新抛出Assert.Fail()。 但实际上,这表明您不应该手写此内容; 检查您的测试框架的选项,或者看看是否可以抛出更有意义的异常来进行测试。
您应该能够根据自己的喜好调整此方法 - 包括指定要捕获的异常类型。 如果您只期望某些类型,请使用以下命令完成
catch
块: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:
As @Jonas points out, this DOES NOT work for catching a base Exception:
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.
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:如果你使用 NUNIT,你可以这样做:
还可以存储抛出的异常以进一步验证它:
请参阅:http://nunit .org/docs/2.5/exceptionAsserts.html
if you use NUNIT, you can do something like this:
It is also possible to store the thrown exception in order to validate it further:
See: http://nunit.org/docs/2.5/exceptionAsserts.html
我首选的实现方法是编写一个名为 Throws 的方法,并像任何其他 Assert 方法一样使用它。 不幸的是,.NET 不允许您编写静态扩展方法,因此您无法使用该方法,就好像它实际上属于 Assert 类中的构建一样; 只需制作另一个名为 MyAssert 或类似的东西即可。 该类看起来像这样:
这意味着您的单元测试看起来像这样:
其外观和行为更像单元测试语法的其余部分。
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:
That means that your unit test looks like this:
Which looks and behaves much more like the rest of your unit test syntaxes.
MSTest (v2) 现在有一个
Assert.ThrowsException()
函数,可以像这样使用:您可以使用 Nuget 安装它:
Install-Package MSTest.TestFramework
。MSTest (v2) now has an
Assert.ThrowsException()
function which can be used like this:You can install it with Nuget:
Install-Package MSTest.TestFramework
.如果您使用的是 MSTest,它最初没有
ExpectedException
属性,您可以这样做:If you're using MSTest, which originally didn't have an
ExpectedException
attribute, you could do this:请谨慎使用 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 thanExpectedException
.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/
在我正在进行的一个项目中,我们有另一个解决方案来做到这一点。
首先,我不喜欢 ExpectedExceptionAttribute,因为它确实考虑了导致异常的方法调用。
我用辅助方法来做到这一点。
测试
HelperMethod
很简单,不是吗;)
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
HelperMethod
Neat, isn´t it ;)
它是测试方法的一个属性...您不使用 Assert。 看起来像这样:
It is an attribute on the test method... you don't use Assert. Looks like this:
您可以通过简单的一行来实现这一点。
如果您的操作
foo.bar()
是异步的:如果
foo.bar()
不是异步的You can achieve this with a simple one-line.
If your operation
foo.bar()
is async:If
foo.bar()
is not async您可以使用以下命令从 Nuget 下载软件包:PM> Install-Package MSTestExtensions,将 nUnit/xUnit 样式的 Assert.Throws() 语法添加到 MsTest。
高级说明:下载程序集并从 BaseTest 继承,您可以使用 Assert.Throws() 语法。
Throws 实现的主要方法如下所示:
披露:我将这个包放在一起。
更多信息: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:
Disclosure: I put together this package.
More Info: http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html
在VS内置单元测试中,如果你只是想验证是否抛出了“任何异常”,但你不知道类型,你可以使用catch all:
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:
我知道这个线程很旧并且有很多好的答案,但也许值得一提的是,本地函数可以以非常简单的方式提供帮助。
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.
我不建议使用 ExpectedException 属性(因为它太受限制且容易出错)或在每个测试中编写 try/catch 块(因为它太复杂且容易出错)。 使用设计良好的断言方法——由您的测试框架提供或您自己编写。 这是我写的和使用的。
使用示例:
注意
返回异常而不是支持验证回调是一个合理的想法,只不过这样做会使该断言的调用语法与我使用的其他断言非常不同。
与其他人不同,我使用“传播”而不是“抛出”,因为我们只能测试异常是否从调用传播。 我们无法直接测试是否抛出异常。 但我想你可以将 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.
Example uses:
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.
好吧,我会总结一下这里其他人之前所说的...无论如何,这是我根据好的答案构建的代码:)剩下要做的就是复制和使用...
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...
上面@Richiban 提供的帮助器效果很好,只是它不处理抛出异常的情况,但不是预期的类型。 以下内容涉及:
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:
由于您提到使用其他测试类,因此比
ExpectedException
属性更好的选择是使用 Shoudly< /a> 的 Should.Throw。假设我们要求客户必须有地址才能创建订单。 如果不是,
CreateOrderForCustomer
方法应导致ArgumentException
。 然后我们可以这样写:这比使用
ExpectedException
属性更好,因为我们正在具体说明什么应该抛出错误。 这使得我们测试中的要求更加清晰,也使得测试失败时的诊断更加容易。请注意,还有一个用于异步方法测试的
Should.ThrowAsync
。Since you mention using other test classes, a better option than the
ExpectedException
attribute is to use Shoudly's Should.Throw.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 anArgumentException
. Then we could write: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.作为替代方案,您可以尝试测试实际上在测试中的接下来的两行中抛出的异常。
As an alternative you can try testing exceptions are in fact being thrown with the next 2 lines in your test.
有一个很棒的库,名为 NFluent ,它可以加快并简化您编写断言的方式。
编写抛出异常的断言非常简单:
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:
FluentAssertions 示例
为使用该库的用户添加使用
FluentAssertions
的示例。异步示例
FluentAssertions Examples
Adding an example using
FluentAssertions
for those using that library.Async Example
这将取决于您使用什么测试框架?
例如,在 MbUnit 中,您可以使用属性指定预期的异常,以确保您获得真正预期的异常。
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.
如果使用 NUnit,请尝试以下操作:
In case of using NUnit, try this:
查看 nUnit 文档 了解以下示例:
Check out nUnit Docs for examples about:
尽管这是一个老问题,但我想在讨论中添加一个新的想法。 我已将“安排、行动、断言”模式扩展为“预期、安排、行动、断言”。 您可以创建一个预期的异常指针,然后断言它被分配给。 这感觉比在 catch 块中执行断言更干净,而将 Act 部分主要留给一行代码来调用被测试的方法。 您也不必从代码中的多个点进行
Assert.Fail();
或return
。 抛出的任何其他异常都将导致测试失败,因为它不会被捕获,并且如果抛出了您期望类型的异常,但它不是您期望的异常,则针对消息或其他属性进行断言异常有助于确保您的测试不会无意中通过。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();
orreturn
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.这适用于 Visual Studio Team Test(又名 MSTest)
在处理数据库或http事务时。 系统应该在某个地方抛出异常,使用 Assert.ThrowExceptionAsync<>() 将捕获您的 Throw 事件。 (在这些情况下,Assert.ThrowException<>() 不会捕获异常)。
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).