如何在MSTest中捕获异常而不抓狂?
\n\n您正在编写C#单元测试,突然发现:您的方法在特定情况下应该抛出异常。如何验证这一点?如果您曾搜索过“How do I use Assert to verify that an exception has been thrown with MSTest”,那么您并不孤单——这是Stack Overflow上最热门的问题之一。今天我们将一劳永逸地解决它。
\n\n在本文中,我将向您展示MSTest中检查异常的所有方法:从经典属性到现代的Assert.ThrowsException。您将学习如何编写干净、可靠的测试,这些测试不会在最不合时宜的时刻失败。我们开始吧!
为什么检查异常很重要?
\n\n想象一下:您有一个方法Divide(int a, int b)。如果b = 0,它应该抛出DivideByZeroException。如果没有测试,只有当应用程序在生产环境中崩溃时您才会知道。带有异常检查的单元测试就是您的安全绳。
在MSTest(微软的单元测试框架)中,有几种方法。我们将逐一介绍,但首先从最现代且推荐的方法开始。
\n\n方法1:Assert.ThrowsException(最正确的方法)
\n\n从MSTest V2(2.x版本)开始,我们有了内置方法Assert.ThrowsException<T>。它优雅、可读且灵活。语法很简单:
[TestMethod]\npublic void Divide_ByZero_ThrowsDivideByZeroException()\n{\n // Arrange\n var calculator = new Calculator();\n\n // Act & Assert\n Assert.ThrowsException<DivideByZeroException>(() => calculator.Divide(10, 0));\n}\n\n这里发生了什么?我们传递一个包含方法调用的lambda表达式。MSTest在try-catch块内执行它。如果没有抛出异常——测试失败。如果抛出了其他类型的异常——同样失败。只有预期的异常才会导致成功。
\n\n高级示例:检查异常消息
\n\n有时不仅需要确认异常的类型,还需要确认其消息。为此,请使用带有message参数的重载,或检查异常对象的属性:
[TestMethod]\npublic void GetUserById_InvalidId_ThrowsWithCorrectMessage()\n{\n // Arrange\n var service = new UserService();\n\n // Act\n var exception = Assert.ThrowsException<ArgumentException>(() => service.GetUserById(-1));\n\n // Assert额外检查\n Assert.AreEqual("Id must be positive", exception.Message);\n}\n\n请注意:Assert.ThrowsException会返回异常对象!这使您可以检查它的任何属性:InnerException、Data、StackTrace(尽管通常不检查最后一个)。
方法2:[ExpectedException]属性(已过时,但仍在使用)
\n\n以前(在MSTest V1中),在测试方法本身上使用属性。看起来像这样:
\n\n[TestMethod]\n[ExpectedException(typeof(DivideByZeroException))]\npublic void Divide_ByZero_ThrowsDivideByZeroException_OldWay()\n{\n var calculator = new Calculator();\n calculator.Divide(10, 0);\n}\n\n看起来方便?但存在三个问题:
\n- \n
- 无法检查异常消息。您不知道抛出的异常是否正确。 \n
- 任何此类型的异常都算成功。即使它来自代码的其他行(例如,在Arrange中)。 \n
- 无法在异常后执行代码。测试方法会立即中断。 \n
因此,在新项目中使用Assert.ThrowsException。[ExpectedException]属性已被标记为deprecated(已过时),可能会在将来版本中被移除。
方法3:手动try-catch(适合极限玩家)
\n\n有时(非常罕见)您需要