views:

772

answers:

7
+6  Q: 

Testing exceptions

do you thing this is a good way for testing exception? any suggestions ?

Exception exception = null;
        try
        {
            //I m sure that an exeption will happen here
        }
        catch (Exception ex)
        {
            exception = ex;
        }

        Assert.IsNotNull(exception);

Im using MS Test

+1  A: 

Perhaps you could just use Assert.Fail after the code that should throw the exception.

Volte
That does the opposite, i.e. it asserts that an exception *wasn't* thrown.
Rob
Yep sorry, I misread the Assert.IsNotNull call and reversed my logic.
Volte
+11  A: 

One way to test exceptions is to put an "Assert.Fail()" after code that should have thrown an exception. If your code reaches the Assert.Fail, it means that an exception was not thrown and the test fails.

try
{
    SomethingThatCausesAnException();
    Assert.Fail("Should have exceptioned above!");
}
catch (InvalidOperationException ex) // must catch specific type of Exception you're testing, not just Exception as allgeek points out
{
    // whatever logging code
}
Andy White
Now that I have the reputation to comment, I figured I should point out my reply below - this example contains a critical flaw that will prevent the test from properly reporting failures.Remember when unit testing, it's vital to test and verify failure cases so you know you're testing right!
allgeek
As allgeek points out, that won't work as Assert.Fail() actually operates by raising an exception (e.g. a NUnit.Framework.AssertionException)
GrahamS
Ah, good point, I didn't even think about that. I'll edit the response, I guess it's best to catch the specific exception that you're trying to test - catching "Exception" will not work as you state.
Andy White
+2  A: 

Mark the test with the ExpectedExceptionAttribute (this is the term in NUnit or MSTest; users of other unit testing frameworks may need to translate).

itowlson
+2  A: 

With most .net unit testing frameworks you can put an [ExpectedException] attribute on the test method. However this can't tell you that the exception happened at the point you expected it to. That's where xunit.net can help.

With xunit you have Assert.Throws, so you can do things like this:

 [Fact]
 public void CantDecrementBasketLineQuantityBelowZero()
 {
  var o = new Basket();
  var p = new Product {Id = 1, NetPrice = 23.45m};
  o.AddProduct(p, 1);
  Assert.Throws<BusinessException>(() => o.SetProductQuantity(p, -3));
 }

[Fact] is the xunit equivalent of [TestMethod]

Steve Willcock
+9  A: 

I have a couple of different patterns that I use. I use the ExpectedException attribute most of the time when an exception is expected. This suffices for most cases, however, there are some cases when this is not sufficient. The exception may not be catchable -- since it's thrown by a method that is invoked by reflection -- or perhaps I just want to check that other conditions hold, say a transaction is rolled back or some value has still been set. In these cases I wrap it in a try/catch block that expects the exact exception, does an Assert.Fail if the code succeeds and also catches generic exceptions to make sure that a different exception is not thrown.

First case:

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void MethodTest()
{
     var obj = new ClassRequiringNonNullParameter( null );
}

Second case:

[TestMethod]
public void MethodTest()
{
    try
    {
        var obj = new ClassRequiringNonNullParameter( null );
        Assert.Fail("An exception should have been thrown");
    }
    catch (ArgumentNullException ae)
    {
        Assert.AreEqual( "Parameter cannot be null or empty.", ae.Message );
    }
    catch (Exception e)
    {
        Assert.Fail(
             string.Format( "Unexpected exception of type {0} caught: {1}",
                            e.GetType(), e.Message )
        );
    }
}
tvanfosson
Many unit testing frameworks implement assertion failures as exceptions. So the Assert.Fail() in the second case will get caught by the catch(Exception) block, which will hide the exception message. You need to add a catch(NUnit.Framework.AssertionException){throw;} or similar - see my answer.
GrahamS
@Graham -- I typed this off the top of my head. Ordinarily I would also print out the exception message in addition to it's type. The point is that the test would fail since the second handler would catch the assertion failure and "refail" with information on the error.
tvanfosson
+1  A: 

As an alternative to using ExpectedException attribute, I sometimes define two helpful methods for my test classes:

AssertThrowsException() takes a delegate and asserts that it throws the expected exception with the expected message.

AssertDoesNotThrowException() takes the same delegate and asserts that it does not throw an exception.

This pairing can be very useful when you want to test that an exception is thrown in one case, but not the other.

Using them my unit test code might look like this:

ExceptionThrower callStartOp = delegate(){ testObj.StartOperation(); };

// Check exception is thrown correctly...
AssertThrowsException(callStartOp, typeof(InvalidOperationException), "StartOperation() called when not ready.");

testObj.Ready = true;

// Check exception is now not thrown...
AssertDoesNotThrowException(callStartOp);

Nice and neat huh?

My AssertThrowsException() and AssertDoesNotThrowException() methods are defined on a common base class as follows:

protected delegate void ExceptionThrower();

/// <summary>
/// Asserts that calling a method results in an exception of the stated type with the stated message.
/// </summary>
/// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param>
/// <param name="expectedExceptionType">The expected type of the exception, e.g. typeof(FormatException).</param>
/// <param name="expectedExceptionMessage">The expected exception message (or fragment of the whole message)</param>
protected void AssertThrowsException(ExceptionThrower exceptionThrowingFunc, Type expectedExceptionType, string expectedExceptionMessage)
{
    try
    {
        exceptionThrowingFunc();
        Assert.Fail("Call did not raise any exception, but one was expected.");
    }
    catch (NUnit.Framework.AssertionException)
    {
        // Ignore and rethrow NUnit exception
        throw;
    }
    catch (Exception ex)
    {
        Assert.IsInstanceOfType(expectedExceptionType, ex, "Exception raised was not the expected type.");
        Assert.IsTrue(ex.Message.Contains(expectedExceptionMessage), "Exception raised did not contain expected message. Expected=\"" + expectedExceptionMessage + "\", got \"" + ex.Message + "\"");
    }
}

/// <summary>
/// Asserts that calling a method does not throw an exception.
/// </summary>
/// <remarks>
/// This is typically only used in conjunction with <see cref="AssertThrowsException"/>. (e.g. once you have tested that an ExceptionThrower
/// method throws an exception then your test may fix the cause of the exception and then call this to make sure it is now fixed).
/// </remarks>
/// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param>
protected void AssertDoesNotThrowException(ExceptionThrower exceptionThrowingFunc)
{
    try
    {
        exceptionThrowingFunc();
    }
    catch (NUnit.Framework.AssertionException)
    {
        // Ignore and rethrow any NUnit exception
        throw;
    }
    catch (Exception ex)
    {
        Assert.Fail("Call raised an unexpected exception: " + ex.Message);
    }
}
GrahamS
+4  A: 

I'm new here and don't have the reputation to comment or downvote, but wanted to point out a flaw in the example in Andy White's reply:

try
{
    SomethingThatCausesAnException();
    Assert.Fail("Should have exceptioned above!");
}
catch (Exception ex)
{
    // whatever logging code
}

In all unit testing frameworks I am familiar with, Assert.Fail works by throwing an exception, so the generic catch will actually mask the failure of the test. If SomethingThatCausesAnException() does not throw, the Assert.Fail will, but that will never bubble out to the test runner to indicate failure.

If you need to catch the expected exception (i.e., to assert certain details, like the message / properties on the exception), it's important to catch the specific expected type, and not the base Exception class. That would allow the Assert.Fail exception to bubble out (assuming you aren't throwing the same type of exception that your unit testing framework does), but still allow validation on the exception that was thrown by your SomethingThatCausesAnException() method.

allgeek