276

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

像这样的东西:

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

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

4

9 回答 9

505

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以确保抛出的消息使用预期的文化。如果您将异常消息存储为资源以允许本地化,这将发挥作用。

于 2009-10-22T19:49:19.593 回答
25

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

[Test]
[ExpectedException(typeof(InvalidOperationException), 
 ExpectedMessage="You can't do that!"]
public void MethodA_WithNull_ThrowsInvalidOperationException()
{
    MethodA(null);
}
于 2010-12-23T10:06:11.743 回答
15
Assert.That(myTestDelegate, Throws.ArgumentException
    .With.Property("Message").EqualTo("your argument is invalid."));
于 2013-12-04T02:33:59.993 回答
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;.

于 2019-01-18T11:05:00.297 回答
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"));
于 2013-03-08T20:41:00.260 回答
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); }));

Assert 中有更多内容表明发生了特定异常(MSTest 中的 Assert.Throws)

于 2011-03-02T07:28:46.983 回答
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");
于 2017-02-09T15:24:07.033 回答
1

对于那些使用NUnit 3.0 约束模型并在这里结束的人:

Assert.That(() => MethodUnderTest(someValue), Throws.TypeOf<ArgumentException>());
于 2021-04-18T15:41:04.797 回答
0

断言异常:

在 6 月 5 日:

    @Test
    public void whenExceptionThrown_thenAssertionSucceeds() {
        Exception exception = assertThrows(NumberFormatException.class, () -> {
            Integer.parseInt("1a");
        });
    
        String expectedMessage = "For input string";
        String actualMessage = exception.getMessage();
    
        assertTrue(actualMessage.contains(expectedMessage));
    }

In Junit 4 :


@Test(expected = NullPointerException.class)
public void whenExceptionThrown_thenExpectationSatisfied() {
    String test = null;
    test.length();
}
于 2022-02-09T07:01:37.620 回答