Wednesday, September 29, 2010

Testing exceptions in unit test

NUnit has a nice feature (Assert.Throws) that makes it possible to assert that a exception is thrown inside some code.

Visual Studio Unit Testing Framework seem to miss this feature and to check that the correct exception is thrown you would could use the following code:
[TestMethod]
public void WithTryCatch()
{
    // Arrange
    ApplicationException actualException = null;

    // Act
    try
    {
        ThrowSomeException();
    }
    catch (ApplicationExceptionex)
    {
        actualException = ex;
    }

    // Assert
    Assert.IsNotNull(actualException);
    Assert.AreEqual("Message", actualException.Message);
}

ExpectedExceptionAttribute
Using the ExpectedExceptionAttribute is possible. In the example below you expect a exception of type ApplicationException with the message set to "ExceptionMessage". All derived exceptions from ApplicationException will also satisfy the test.
[TestMethod]
[ExpectedException(typeof(ApplicationException), "ExceptionMessage", true)]
public SomeTest()
{
    DoSomething();
}

ExceptionAssert
The ExpectedExceptionAttribute could be enough in many situations. But it will not make you able to test specific attributes on an exception or the value of its inner exception. So why not make things it a little easier and more flexible with a helper class.
[TestMethod]
public void WithHelperClass()
{
    // Arrange
    // Act
    // Assert
    ExceptionAssert.Throws<ApplicationException>(
        () => ThrowSomeException(),
        ex => Assert.AreEqual("Message", ex.Message));
}
The Throws method catches the exception specified and will run the asserts on it. If no (or wrong) exception is thrown the test will fail.

The source code for the helper class can be found below.
[DebuggerStepThrough]
public static class ExceptionAssert
{
 /// 
 /// Asserts that an exception of type T is not thrown
 /// 
 /// >Typeparam name="T">Exception to look for
 /// Action to execute
 public static void DoesNotThrow<T>(Action action) where T : Exception
 {
  if (action == null)
  {
   throw new ArgumentNullException("action");
  }

  Exception actualException = null;
  try
  {
   action();
  }
  catch (T ex)
  {
   actualException = ex;
  }

  if (actualException != null)
  {
   throw new AssertFailedException(String.Format(
    "ExceptionAssert.DoesNotThrow failed. Exception <{0}> thrown with message <{1}>",
    actualException.GetType().FullName,
    actualException.Message));
  }
 }

 /// 
 /// Asserts that an exception is not thrown
 /// 
 /// Action to execute
 public static void DoesNotThrow(Action action)
 {
  DoesNotThrow(action);
 }

 /// 
 /// Asserts that an exception of type T is thrown
 /// 
 /// Exception to look for
 /// Action to execute
 public static void Throws<T>(Action action) where T : Exception
 {
  if (action == null)
  {
   throw new ArgumentNullException("action");
  }

  Exception actualException = null;
  try
  {
   action();
  }
  catch (Exception ex)
  {
   actualException = ex;
  }

  ValidateThrownException<T>(actualException, null);
 }

 /// 
 /// Asserts that an exception of type T is thrown
 /// 
 /// Exception to look for
 /// Action to execute
 /// Additional assert to be made on the exception
 public static void Throws<T>(Action action, Action<T> asserts) where T : Exception
 {
  if (action == null)
  {
   throw new ArgumentNullException("action");
  }

  Exception actualException = null;
  try
  {
   action();
  }
  catch (Exception ex)
  {
   actualException = ex;
  }

  ValidateThrownException(actualException, asserts);
 }

 /// 
 /// Asserts that an exception of type T is thrown
 /// 
 /// Exception to look for
 /// Action to execute
 /// Additional assert to be made on the exception
 /// Cleanup action to be executed.
 public static void Throws<T>(Action action, Action<T> asserts, Action finalAction) where T : Exception
 {
  if (action == null)
  {
   throw new ArgumentNullException("action");
  }

  Exception actualException = null;
  try
  {
   action();
  }
  catch (Exception ex)
  {
   actualException = ex;
  }
  finally
  {
   if (finalAction != null)
   {
    finalAction();
   }
  }

  ValidateThrownException(actualException, asserts);
 }

 /// 
 /// Valdidates the exception
 /// 
 /// Exception type to look for
 /// Exception to validate
 /// Additional asserts to be made on the exception
 private static void ValidateThrownException<T>(Exception actualException, Action<T> asserts) where T : Exception
 {
  if (actualException is T)
  {
   if (asserts != null)
   {
    asserts(actualException as T);
   }
  }
  else if (actualException == null)
  {
   throw new AssertFailedException(String.Format(
    "ExceptionAssert.Throws failed. No exception was thrown. Expected <{0}>.",
    typeof(T).FullName));
  }
  else
  {
   throw new AssertFailedException(String.Format(
    "ExceptionAssert.Throws failed. Expected <{0}>. Actual <{1}>",
    typeof(T).FullName,
    actualException.GetType().FullName));
  }
 }
}

Share:

0 kommentarer:

Post a Comment