Testing for Exceptions in MSTest with Assert Methods

Introduction to Exception Testing in MSTest

When developing software, ensuring that your code handles unexpected situations gracefully is critical. One way to verify this behavior is by testing if specific exceptions are thrown under certain conditions. In .NET’s Microsoft.VisualStudio.TestTools.UnitTesting (MSTest) framework, you have multiple options for asserting that an exception has been thrown during a test. This tutorial will guide you through different methods of verifying exceptions in MSTest.

Understanding Exception Testing

Testing for exceptions is a way to validate that your application correctly handles errors and invalid states by throwing expected exceptions. It helps ensure robustness and reliability, making sure that your code fails gracefully when something goes wrong.

Method 1: Using the ExpectedException Attribute

One of the simplest ways to test if an exception is thrown is using the ExpectedException attribute. This method is straightforward but less flexible since it only checks for a specific type of exception and doesn’t allow further inspection of the exception’s properties.

Example:

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class ExceptionTests
{
    [TestMethod]
    [ExpectedException(typeof(ArgumentException), "A userId of null was inappropriately allowed.")]
    public void NullUserIdInConstructor()
    {
        LogonInfo logonInfo = new LogonInfo(null, "P@ss0word");
    }
}

Method 2: Using Try-Catch Blocks with Assert.Fail

If your testing framework lacks specific exception assertions, you can manually implement it using try-catch blocks combined with Assert.Fail. This approach allows for more control but requires careful handling to avoid swallowing exceptions unintentionally.

Example:

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class ExceptionTests
{
    [TestMethod]
    public void NullParameterShouldThrowException()
    {
        try
        {
            somethingThatShouldThrowAnException();
            Assert.Fail("Expected exception was not thrown.");
        }
        catch (SpecificException)
        {
            // Expected exception caught
        }
        catch (Exception ex)
        {
            Assert.Fail($"Unexpected exception type: {ex.GetType().Name}");
        }
    }
}

Method 3: Custom Extension Methods

For more seamless integration with MSTest, you can create custom extension methods. While .NET does not allow static extension methods in the global namespace, you can add them to a static class within your test project.

Implementation:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        public static void Throws<T>(Action func) where T : Exception
        {
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch (T)
            {
                exceptionThrown = true;
            }

            if (!exceptionThrown)
            {
                throw new AssertFailedException($"An exception of type {typeof(T)} was expected, but not thrown");
            }
        }
    }
}

Usage:

[TestClass]
public class ExceptionTests
{
    [TestMethod()]
    public void ExceptionTest()
    {
        string testStr = null;
        MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper());
    }
}

Method 4: Using Assert.ThrowsException (MSTest v2)

With the release of MSTest v2, a built-in method, Assert.ThrowsException, was introduced. This method is part of the MSTest.TestFramework package and can be installed via NuGet.

Example:

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class ExceptionTests
{
    [TestMethod]
    public void FormatExceptionTest()
    {
        Assert.ThrowsException<System.FormatException>(() =>
        {
            Story actual = PersonalSite.Services.Content.ExtractHeader(string.Empty);
        });
    }
}

Best Practices and Tips

  • Choose the Right Method: Select a method based on your needs. ExpectedException is simple but limited, while custom methods offer more flexibility.
  • Avoid Over-Catching Exceptions: Be careful not to catch exceptions too broadly unless you’re specifically testing for them.
  • Consider Future Maintenance: Using built-in MSTest functionalities can make your tests easier to maintain and understand.

Conclusion

Testing for exceptions is a critical part of ensuring the robustness of your software. By using MSTest, you have several approaches at your disposal to assert that exceptions are thrown under specific conditions. Whether you opt for attributes, try-catch blocks, custom methods, or built-in functionalities, each method offers unique advantages tailored to different scenarios.

Leave a Reply

Your email address will not be published. Required fields are marked *