ChatGPT解决这个技术问题 Extra ChatGPT

How do I use Assert.Throws to assert the type of the exception?

How do I use Assert.Throws to assert the type of the exception and the actual message wording?

Something like this:

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

The method I am testing throws multiple messages of the same type, with different messages, and I need a way to test that the correct message is thrown depending on the context.


B
B4rT

Assert.Throws returns the exception that's thrown which lets you assert on the exception.

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

So if no exception is thrown, or an exception of the wrong type is thrown, the first Assert.Throws assertion will fail. However if an exception of the correct type is thrown then you can now assert on the actual exception that you've saved in the variable.

By using this pattern you can assert on other things than the exception message, e.g. in the case of ArgumentException and derivatives, you can assert that the parameter name is correct:

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

You can also use the fluent API for doing these asserts:

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

or alternatively

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

A little tip when asserting on exception messages is to decorate the test method with the SetCultureAttribute to make sure that the thrown message is using the expected culture. This comes into play if you store your exception messages as resources to allow for localization.


This was really helpful for me - I wanted a way to display the error, I did not even read if a value was returned by the Assert.Throws method. Thanks
+1 Thank you for showing the Fluent API, for some reason I was having problems understanding how to use it just from the NUnit documents alone.
When you want to assert the message, you can also directly use the Message property instead of the "Property".
Whats the difference between Assert.That(SomeMethod, Throws.ArgumentException); and Assert.That(() => SomeMethod, Throws.ArgumentException);
S
SteveC

You can now use the ExpectedException attributes, e.g.

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

This disturbed me a little when first seen, because the test apparentl had no assert, which was a smell to me. This is a nice feature, but one should discuss in the team wheter this attribut should get used over the Assert.Throws
+1 also a nice way to test for exceptions. The only thing to keep in mind about this is that theoretically any line of code throwing an InvalidOperationException with that message will pass the test, including code in your test which prepares the test data/objects or any other method you might need to execute before the one you are interested in testing, possibly resulting in a false positive. Off course, that depends how specific the message is and the type of exception you are testing. With Assert.Throw you can target the exact line you are interested in.
ExpectedException attribute is deprecated in NUnit 3: github.com/nunit/docs/wiki/Breaking-Changes
I don't like this approach. With this approach any line in your code that throws exception passess the test, even if it not supposed to throw. Assert.Throws is more ideal as you can assert specific method call. Usually unit tests are not just one line of code anyway. So why write constraints for all lines instead of the one you want?
J
Jordan Morris
Assert.That(myTestDelegate, Throws.ArgumentException
    .With.Property("Message").EqualTo("your argument is invalid."));

With the introduction of the nameof operator I would edit this excellent anwser to: Assert.That(myTestDelegate, Throws.ArgumentException .With.Property(nameof(ArgumentException.Message)).EqualTo("your argument is invalid."));
@Samuel That edit would use a strongly typed reference which is nice, but on the other hand, it's an extremely low-churn property name and the magic string improves fluency. A matter of taste I suppose
I entirely agree with you regarding Exception.Message. I still would recommend to at least add this alternative because With.Property can be utilized on other objects too, which those this case would improve the stability of the code.
I'd rather use Throws.Exception.TypeOf<ArgumentNullException>().With.Message.EqualTo("Value cannot be null.\r\nParameter name: password") instead of .With.Property("Message").
@AlexMelw agreed, .Message is not always available, depending on the context
P
Peter Mortensen

A solution that actually works:

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);
}

This works with using Microsoft.VisualStudio.TestTools.UnitTesting;.


I was really surprised that there was no concise way to assert that a method throws an exception like in JUnit. Unless there are implications I'm not aware of, this is probably the most relevant answer currently.
This answer is for MsTest, the question is about NUnit.
Is it really for MSTest?
P
Peter Mortensen

To expand on persistent's answer, and to provide more of the functionality of NUnit, you can do this:

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;
}

Examples:

// 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"));

S
Savage

Since I'm disturbed by the verbosity of some of the new NUnit patterns, I use something like this to create code that is cleaner for me personally:

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);
}

The usage is then:

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

What is TestDelegate?
It allows you to pass the code to execute as a parameter. It's a class in the NUnit framework (v3.2.0.0).
P
Peter Mortensen

I recently ran into the same thing, and suggest this function for MSTest:

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

Usage:

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

There is more in Assert that a particular exception has occured (Assert.Throws in MSTest).


D
Dustin_00

For those that are using the NUnit 3.0 Constraint Model and ended up here:

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

S
Sandeep Jain

Asserting exception :

In Junit 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();
}