311

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
epitka
  • 17,275
  • 20
  • 88
  • 141

9 Answers9

546

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.

B4rT
  • 3
  • 2
Patrik Hägne
  • 16,751
  • 5
  • 52
  • 60
  • 1
    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 – Haroon Jun 01 '12 at 13:42
  • 7
    +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. – aolszowka Oct 19 '13 at 20:44
  • When you want to assert the message, you can also directly use the Message property instead of the "Property". – Marcel May 24 '19 at 11:08
  • Whats the difference between `Assert.That(SomeMethod, Throws.ArgumentException);` and `Assert.That(() => SomeMethod, Throws.ArgumentException);` – Dean P Feb 01 '22 at 16:02
  • `int? id = null; Assert.Throws(() => sutPatientRepository.Object.GetPatientByID(id));` this do not throw exception and test is failing, the method under test looks like this `public Patient GetPatientByID(int? id) { if(id== null) throw new ArgumentNullException("id");}` what am i missing? @Patrick – GKhedekar May 22 '23 at 11:32
27

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);
}
SteveC
  • 15,808
  • 23
  • 102
  • 173
Jackson Pope
  • 14,520
  • 6
  • 56
  • 80
  • 3
    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 – Marcel Jan 30 '13 at 13:18
  • 14
    +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. – Nope Mar 04 '13 at 10:06
  • 26
    ExpectedException attribute is deprecated in NUnit 3: https://github.com/nunit/docs/wiki/Breaking-Changes – Frank Sebastià Jun 08 '16 at 09:01
  • 1
    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? – John Demetriou Jan 19 '21 at 12:11
17
Assert.That(myTestDelegate, Throws.ArgumentException
    .With.Property("Message").EqualTo("your argument is invalid."));
Jordan Morris
  • 2,101
  • 2
  • 24
  • 41
  • 2
    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 Jul 27 '18 at 15:45
  • @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 – Jordan Morris Jul 31 '18 at 23:01
  • 1
    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. – Samuel Aug 01 '18 at 06:30
  • An explanation would be in order. – Peter Mortensen Jul 28 '20 at 19:52
  • 1
    I'd rather use `Throws.Exception.TypeOf().With.Message.EqualTo("Value cannot be null.\r\nParameter name: password")` instead of `.With.Property("Message").` – AlexMelw Apr 30 '21 at 12:23
  • 1
    @AlexMelw agreed, .Message is not always available, depending on the context – Jordan Morris May 23 '21 at 22:27
11

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tvde1
  • 1,246
  • 3
  • 21
  • 41
5

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

Assert.That(() => MethodUnderTest(someValue), Throws.TypeOf<ArgumentException>());
Dustin_00
  • 345
  • 2
  • 8
4

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"));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
fre0n
  • 1,885
  • 1
  • 20
  • 26
2

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");
Savage
  • 2,296
  • 2
  • 30
  • 40
2

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).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
persistent
  • 190
  • 1
  • 9
1

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();
}
Timothy G.
  • 6,335
  • 7
  • 30
  • 46
Sandeep Jain
  • 1,019
  • 9
  • 13