如何使用Assert.Throws声明异常的类型?


245

我如何使用Assert.Throws断言的类型和实际的消息措辞。

像这样:

Assert.Throws<Exception>(
    ()=>user.MakeUserActive()).WithMessage("Actual exception message")

我正在测试的方法会抛出具有不同消息的相同类型的多个消息,并且我需要一种方法来测试是否根据上下文抛出正确的消息。

Answers:


442

Assert.Throws 返回引发的异常,使您可以断言该异常。

var ex = Assert.Throws<Exception>(() => user.MakeUserActive());
Assert.That(ex.Message, Is.EqualTo("Actual exception message"));

因此,如果没有引发异常,或者引发了错误类型的异常,则第一个Assert.Throws断言将失败。但是,如果抛出了正确类型的异常,那么您现在可以对已保存在变量中的实际异常进行断言。

通过使用此模式,您可以断言除异常消息以外的其他内容,例如,对于ArgumentException和派生,可以断言参数名称正确:

var ex = Assert.Throws<ArgumentNullException>(() => foo.Bar(null));
Assert.That(ex.ParamName, Is.EqualTo("bar"));

您还可以使用fluent API进行以下声明:

Assert.That(() => foo.Bar(null), 
Throws.Exception
  .TypeOf<ArgumentNullException>()
  .With.Property("ParamName")
  .EqualTo("bar"));

或者

Assert.That(
    Assert.Throws<ArgumentNullException>(() =>
        foo.Bar(null)
    .ParamName,
Is.EqualTo("bar"));

断言异常消息时的一个小技巧是用修饰装饰测试方法,SetCultureAttribute以确保抛出的消息使用了预期的区域性。如果您将异常消息存储为允许本地化的资源,则这会起作用。


这对我真的很有帮助-我想要一种显示错误的方法,如果Assert.Throws方法返回了值,我什至没有读取。谢谢
Haroon 2012年

6
+1感谢您展示Fluent API,由于某些原因,我在仅从NUnit文档中了解如何使用它时遇到了问题。
aolszowka

当您要声明消息时,还可以直接使用Message属性代替“ Property”。
马塞尔(Marcel)

25

您现在可以使用ExpectedException属性,例如

[Test]
[ExpectedException(typeof(InvalidOperationException), 
 ExpectedMessage="You can't do that!"]
public void MethodA_WithNull_ThrowsInvalidOperationException()
{
    MethodA(null);
}

2
初次见面时,这让我有些不安,因为测试表象没有断言,这对我来说是一种气味。这是一个不错的功能,但是应该在团队中讨论一下该属性应在Assert.Throw上使用。投掷
Marcel

14
+1也是测试异常的好方法。唯一需要记住的是,从理论上讲,任何带有该消息的InvalidOperationException抛出的代码行都将通过测试,包括测试中准备测试数据/对象的代码或您可能需要在执行之前执行的任何其他方法。您对测试感兴趣的一个,可能会导致误报。当然,这取决于消息的具体程度和要测试的异常类型。有了Assert.Throw你可以针对精确的线你感兴趣的
都能跟得上

21
NUnit 3中不推荐使用ExpectedException属性: github.com/nunit/docs/wiki/Breaking-Changes
FrankSebastià16年

13
Assert.That(myTestDelegate, Throws.ArgumentException
    .With.Property("Message").EqualTo("your argument is invalid."));

2
通过引入nameof运算符,我将把这个出色的答案编辑为:Assert.That(myTestDelegate, Throws.ArgumentException .With.Property(nameof(ArgumentException.Message)).EqualTo("your argument is invalid."));
Samuel

@Samuel该编辑将使用强类型引用,这很好,但是另一方面,它是一个极低流失的属性名称,并且魔术字符串提高了流畅性。我想
Jordan Morris

1
我完全同意你的看法Exception.Message。我仍然建议至少添加此替代方法,因为它With.Property也可以在其他对象上使用,这种情况下可以提高代码的稳定性。
塞缪尔

5

这是一个古老而又相关的问题,答案已经过时,因此我要添加当前的解决方案:

public void Test() {
    throw new MyCustomException("You can't do that!");
}

[TestMethod]
public void ThisWillPassIfExceptionThrown()
{
    var exception = Assert.ThrowsException<MyCustomException>(
        () => Test(),
        "This should have thrown!");
    Assert.AreEqual("You can't do that!", exception.Message);
}

这适用于 using Microsoft.VisualStudio.TestTools.UnitTesting;


令我惊讶的是,没有一种简明的方法可以断言某个方法会引发像JUnit中的异常。除非有我不知道的含义,否则这可能是当前最相关的答案。
NetherGranite

3

要扩展持久性的答案并提供NUnit的更多功能,可以执行以下操作:

public bool AssertThrows<TException>(
    Action action,
    Func<TException, bool> exceptionCondition = null)
    where TException : Exception 
{
    try
    {
        action();
    }
    catch (TException ex)
    {
        if (exceptionCondition != null)
        {
            return exceptionCondition(ex);
        }

        return true;
    }
    catch
    {
        return false;
    }

    return false; 
}

例子:

// No exception thrown - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => {}));

// Wrong exception thrown - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new ApplicationException(); }));

// Correct exception thrown - test passes.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException(); }));

// Correct exception thrown, but wrong message - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException("ABCD"); },
        ex => ex.Message == "1234"));

// Correct exception thrown, with correct message - test passes.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException("1234"); },
        ex => ex.Message == "1234"));

2

我意识到这个问题已经存在很长时间了,但是我最近遇到了同样的事情,并建议将此功能用于MSTest:

public bool AssertThrows(Action action) where T : Exception 
{ 
try {action();} 
catch(Exception exception) 
{ 
    if (exception.GetType() == typeof(T)) return true; 
} 
return false; 
}

用法:

Assert.IsTrue(AssertThrows<FormatException>(delegate{ newMyMethod(MyParameter); }));

此处更多信息:http : //phejndorf.wordpress.com/2011/02/21/assert-that-a-particular-exception-has-occured/


2

由于我对某些新的NUnit模式的冗长不安感到不安,因此我使用类似这样的代码来创建适合我个人的代码:

public void AssertBusinessRuleException(TestDelegate code, string expectedMessage)
{
    var ex = Assert.Throws<BusinessRuleException>(code);
    Assert.AreEqual(ex.Message, expectedMessage);
}

public void AssertException<T>(TestDelegate code, string expectedMessage) where T:Exception
{
    var ex = Assert.Throws<T>(code);
    Assert.AreEqual(ex.Message, expectedMessage);
}

用法如下:

AssertBusinessRuleException(() => user.MakeUserActive(), "Actual exception message");

1
什么是TestDelegate?
reggaeguitar

1
它允许您传递代码以作为参数执行。这是NUnit框架(v3.2.0.0)中的类。
野人
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.