Testing for exceptions in C#

The traditional method to test for exceptions with the Microsoft unit testing framework is to use the ExpectedException attribute.

This method has shortcomings. The biggest is that it checks the whole method for the exception which can lead to false positives. Here is a contrived example to show what I am talking about.

The StringlyType class is by the way, not serious.

    // class under test
    public class StringlyType
    {
        private StringBuilder stringlyTypeBuilder = null;

        // contrived setup method
        public void Setup()
        {
            stringlyTypeBuilder = new StringBuilder();
        }

        public string Stringlyfy(object toStringlyFy)
        {
            stringlyTypeBuilder.Append(toStringlyFy.ToString());
            return stringlyTypeBuilder.ToString();
        }
    }

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        [ExpectedException(typeof(NullReferenceException))]
        public void TraditionalExpectedException()
        {
            // Arrange
            var classUnderTest = new StringlyType();
            int toStringlyfy = 1;

            // Act
            classUnderTest.Stringlyfy(toStringlyfy);
        }

        [TestMethod]
        [ExpectedException(typeof(NullReferenceException))]
        public void TraditionalExpectedExceptionFalsePositive()
        {
            // Arrange
            var classUnderTest = new StringlyType();

            StringlyType randomClass = null;
            randomClass.Setup();

            int toStringlyfy = 1;

            // Act
            classUnderTest.Stringlyfy(toStringlyfy);
        }

The first test method will pass. We haven’t called the Setup method on StringlyType so the call to Append will throw our NullReferenceException.

On the second test method, we will get a false positive. The call to randomClass.Setup() will throw an exception and our test will be none the wiser. If we change the behaviour of StringlyType so that the StringBuilder is initialized in the constructor, the false positive will keep on telling us the code is throwing an exception.

A better way

NUnit, an open-source alternative to Microsoft’s framework, has been using a better approach for years. Their approach is having an Assert.Throws that works like an ordinary assert.

When I am in an environment that isn’t using NUnit I like to create something equivalent along the lines of:

public static class AssertExtension
    {
        public static T Throws<T>(Action expressionUnderTest,
                                  string exceptionMessage = "Expected exception has not been thrown by target of invocation."
                                 ) where T : Exception
        {
            try
            {
                expressionUnderTest();
            }
            catch (T exception)
            {
                return exception;
            }

            Assert.Fail(exceptionMessage);
            return null;
        }
    }

Since the Assert class is static we can’t create extension methods for it, hence I’m creating another AssertExtension class.

This AssertExtension class will allow to write the previous tests in the following way.

        [TestMethod]
        public void ExampleWithAssertThrows()
        {
            // Arrange
            var underTest = new StringlyType();
            int t = 1;

            // Act && Assert
            AssertExtension.Throws<NullReferenceException>(() => underTest.Stringlyfy(t));
        }

        [TestMethod]
        public void WillNotGetAFalsePositive()
        {
            // Arrange
            var underTest = new StringlyType();

            StringlyType randomClass = null;
            randomClass.Setup();

            int t= 1;

            // Act && Assert
            AssertExtension.Throws<NullReferenceException>(() => underTest.Stringlyfy(t));
        }

Notice that the second method we will not be a false positive.

This method has several advantages:

  • prevents false positives
  • easier to see which statement should throw an exception
  • more succinct and idiomatic than using a try catch
  • allows to return the thrown exception

Here is an example of the last point:

var thrownException = AssertExtension.Throws<InvalidOperationException>(
                      () => classUnderTest.Stringlyfy(toStringlyfy));

Assert.AreEqual("some message", thrownException.Message);

I made a GitHub Gist if you want to get/modify the AssertExtension class.

One thought on “Testing for exceptions in C#

  1. I used the same approach recently. I also created the DoesNotThrow to check nothing is thrown. Both help the reader to understand tests. In the case of DoesNotThrow, I used to have no Assert in the test, and got grief from it. Hence making an obvious statement out of it.
    Here is mine:

    public static class AssertHelper
    {
    static public void DoesNoThrow(Action a, string errorMessage) where T:Exception
    {
    string txt = “”;
    var threw = false;
    try
    {
    a();
    }
    catch (T e)
    {
    threw = true;
    txt = e.Message;
    }
    Assert.IsFalse(threw,string.Format(“Error:{0}\nException Message:\n{1}”,errorMessage,txt));
    }

    static public void Throws(Action a, string errorMessage) where T:Exception
    {
    var threw = false;
    try
    {
    a();
    }
    catch (T content)
    {
    threw = true;
    }
    Assert.IsTrue(threw, errorMessage);
    }
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s