Tuesday, November 24, 2009

Unit Testing Thrown Exceptions

  #region Throwing Exception Tests
        private void MethodToTest()
        {
            throw new ArgumentException("blah");
        }

Using ExpectedException attribute to test exception throwing is pretty straight forward:
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void Test_ExceptionThrown_Attribute()
        {
            try
            {
                MethodToTest();
            }
            catch (Exception ex)
            {
                if (ex.Message == "blah") //test message value
                    throw new ArgumentException(ex.Message);
            }
        }

Another approach might be:

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void Test_ExceptionThrown_Attribute_Function()
        {
            VerifyExceptionThrown_Throw(() => MethodToTest(), "blah"); // simple, single line, and generic.
        }


        // Method accepts an action delegate, a generic Exception type, and an exception message to check
        public static void VerifyExceptionThrown_Throw(Action action, String exMessage)
           where TException : Exception
        {
            // Verify arguments - action can't be null
            if (action == null)
                throw new ArgumentException("Action to test cannot be null!");

            // Verify arguments - TException cannot be of type Exception
            if (typeof(TException).Equals(typeof(Exception)))
                throw new ArgumentException("TException type cannot be of type Exception");

            // Actually call it
            try
            {
                action();
            }
            catch (TException ex)
            {
                if (ex.Message == exMessage)
                {
                    throw new ArgumentException("good job"); //we must rethrow an exception because of ExpectedException attribute 
                }
            }

            // If it gets here, fail the test
            Assert.Fail("Action does not throw the exception expected");
        }

Test for exception throwing without the ExpectedException attribute:

        //
        [TestMethod]
        public void Test_ExceptionThrown_NoAttribute()
        {
            try
            {
                MethodToTest();
                Assert.Fail("Action does not throw the exception expected");
            }
            catch (ArgumentException ex)//specifing the type of exception to test for
            {
                if (ex.Message != "blah") //test message value
                    Assert.Fail("Action does not throw the exception expected"); //failed 
            }
        }

My preferred way to test exceptions:
        [TestMethod]
        public void Test_ExceptionThrown_NoAttribute_Function()
        {
            VerifyExceptionThrown_NoThrow(() => MethodToTest(), "blah"); //simple, generic, message parameter is optional... and the TestMethod doesn't need the attribute
        }


        // Method accepts an action delegate, a generic Exception type, and an exception message to check (optional)
        public static void VerifyExceptionThrown_NoThrow(Action action, String exMessage)
           where TException : Exception
        {
            // Verify arguments - action can't be null
            if (action == null)
                throw new ArgumentException("Action to test cannot be null!");

            // Verify arguments - TException cannot be of type Exception
            if (typeof(TException).Equals(typeof(Exception)))
                throw new ArgumentException("TException type cannot be of type Exception");

            // Actually call it
            try
            {
                action();
            }
            catch (TException ex)
            {
                if (exMessage == null) //don't want to consider the message
                {
                    return;
                }
                else //otherwise consider the message 
                {
                    if (ex.Message == exMessage)
                    {
                        return;
                    }
                }
            }

            // If it gets here, fail the test
            Assert.Fail("Action does not throw the exception expected");
        }
        #endregion

I used code from this example here.

No comments: