如何使用Assert。抛出以断言异常的类型和实际的消息措辞?

就像这样:

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

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


当前回答

要扩展persistent的答案,并提供更多的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"));

其他回答

断言。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装饰测试方法,以确保抛出的消息使用预期的区域性。如果您将异常消息存储为资源以允许本地化,则这将发挥作用。

要扩展persistent的答案,并提供更多的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"));

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

Assert.That(() => MethodUnderTest(someValue), Throws.TypeOf<ArgumentException>());

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

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

由于我被一些新的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");