Waldo sessions now support scripting! – Learn more
App Development

Asserting an Exception in XUnit: A Detailed Guide

Juan Reyes
Juan Reyes
Asserting an Exception in XUnit: A Detailed Guide
February 21, 2023
5
min read

Testing exception-throwing behavior is critical to ensure that your code works as expected, particularly in exceptional scenarios. Exceptions can indicate problems that you need to address, and they can be used to signal errors or extraordinary conditions in your code. By testing the exception-throwing behavior of your code, you can ensure that it behaves correctly in unexpected cases. 

This article will cover how to assert an exception with Microsoft's XUnit testing framework to help you have robust test workflows in your project. 

First, we will introduce the XUnit framework and explain exceptions. Then we will go into the different assertion methods available with the framework. Finally, we will explore how we can handle exceptions in our tests. 

Let's jump right in. 

Introduction to XUnit and Exception Throwing

XUnit is a popular open-source testing framework that's used to write and run unit tests in .NET applications. The framework is designed to be flexible, extensible, and easy to use.

One feature that makes XUnit stand out is its ability to test the exception-throwing behavior of code. This capability is crucial in ensuring that the code behaves as expected in all scenarios. 

Exception throwing is an essential aspect of software development, as it helps to manage the flow of control in an application and handle exceptional scenarios. In .NET, exceptions are instances of the Exception class or one of its derived classes. You can "throw" exceptions by calling the "throw" statement or using the "throw new" expression. 

When an exception is "thrown," the runtime stops the normal execution of the application and transfers control to the nearest catch block. The catch block can handle the exception and take appropriate action, such as logging the error or displaying an error message to the user. If there are no catch blocks to handle the exception, the runtime will propagate the exception to the calling method. Here your code can handle or propagate it further until it reaches the main method, which will terminate the application. 

Additionally, one of the main benefits of XUnit is that it provides several methods for asserting exceptions, giving you the flexibility and control you need to test your code effectively. Let's take a closer look at these methods and how you can use them in your XUnit test workflows.

Assert Exceptions in XUnit

In XUnit, the ability to test the exception-throwing behavior of code is fundamental in designing robust tests. You can do this by using the "Assert.Throws()" method. 

The "Assert.Throws()" method is one of the most commonly used methods for asserting exceptions in XUnit. This method allows you to write a test that checks whether an exception is thrown when executing the code under test. 

The method takes two arguments: the type of exception that is expected to be thrown and a delegate that contains the code you want to test. 

Here's an example of how to use the "Assert.Throws()" method: 


[Fact]
public void TestMethod_ShouldThrowException()
{
    // Arrange
    var exceptionType = typeof(InvalidOperationException);
    // Act and Assert
    Assert.Throws(exceptionType, () => {
        throw new InvalidOperationException();
    });
}

In this example, we are using the "Assert.Throws()" method to test that an InvalidOperationException is thrown when the code under test is executed. 

It's important to note that the "Assert.Throws()" method asserts that an exception of the specified type is thrown. However, it doesn't check the message or other properties of the exception. If you need to validate the properties of the exception, you can use the "Assert.Throws()" method in combination with a lambda expression that captures the exception, like this: 


[Fact]
public void TestMethod_ShouldThrowExceptionWithSpecificMessage()
{
    // Arrange
    var exceptionType = typeof(InvalidOperationException);
    var expectedMessage = "The operation is invalid.";
    // Act and Assert
    var ex = Assert.Throws(() => {
        throw new InvalidOperationException(expectedMessage);
    });
    // Assert
    Assert.Equal(expectedMessage, ex.Message);
}

Here the "Assert.Throws()" method is used to capture the exception that is thrown. Then the exception's message is compared to the expected message to ensure that the exception has the right message. This is very useful for providing more information about what exactly triggered the error and saves you lots of time during your debugging efforts. 

Asserting Exception Messages

In many cases, it is vital to assert not only the type of exception thrown but also the message. XUnit provides several methods for asserting exception messages, including the "Assert.Contains()", "Assert.StartsWith()", "Assert.EndsWith()", and "Assert.Equal()" methods. 

For example, the following code uses the "Assert.Contains()" method to assert that the exception message has a specific string: 


[Fact]
public void TestMethod_ShouldAssertExceptionMessage()
{
    // Arrange
    var exceptionType = typeof(InvalidOperationException);
    var expectedMessage = "The operation is invalid.";
    // Act
    var ex = Record.Exception(() => {
        throw new InvalidOperationException(expectedMessage);
    });
    // Assert
    Assert.Contains("invalid", ex.Message);
}

In this example, we use the "Assert.Contains()" method to assert that the exception message contains the string "invalid." This way, we can ensure that our error messages have the appropriate information. 

How to Check If an Exception Is Thrown in XUnit

In addition to asserting exceptions, XUnit provides several methods that you can use to handle different exceptions in tests. The two most common methods are "Record.Exception()" and "Record.Execution()". These methods allow you to capture and examine the exception thrown in the code under test. 

The "Record.Exception()" method captures an exception and returns it as an object. It does so by taking a delegate as an argument and returning the exception thrown by the delegate or null if no exception is thrown. The caught exception can then be examined, allowing you to make assertions about its type, message, and other properties. 

Here's an example that demonstrates how to use the "Record.Exception()" method to check if an exception is thrown: 


[Fact]
public void TestMethod_ShouldHandleException()
{
    // Arrange
    var exceptionType = typeof(InvalidOperationException);
    var expectedMessage = "The operation is invalid.";
    // Act
    var ex = Record.Exception(() => {
        throw new InvalidOperationException(expectedMessage);
    });
    // Assert
    Assert.NotNull(ex);
    Assert.IsType(exceptionType, ex);
    Assert.Equal(expectedMessage, ex.Message);
}

In this example, the "Record.Exception()" method captures the exception thrown in the code under test. The caught exception is then examined to ensure that it's of the correct type and has a message. This can be done using the Assert class. 

Using the "Record.Exception()" method allows you to quickly test that your code throws the expected exception. Additionally it allows you to check that the exception contains the expected message or other properties. This mechanism is essential to writing robust and practical tests for your code. 

If you want to learn more about testing workflows for .NET or any other technology stack, check out our other articles on our blog.

Conclusion

Testing exception-throwing behavior is an essential aspect of software development. XUnit provides several methods that you can use to assert and handle exceptions in tests. Whether you're using "Assert.Throws()" or "Record.Exception()", XUnit provides the flexibility and extensibility you need to test the exception-throwing behavior of your code. With the right combination of XUnit methods and techniques, you can be confident that your code behaves as expected when faced with exceptional scenarios. 

However, no single approach is devoid of flaws, and certainly, we can't always trust that our process is infallible. After all, we are limited by the time and resources available at any given time. That is why I advise all committed teams to check out Waldo.com's vast UI testing toolkit to ensure their code is reliable and safe. Even for non-developers, it is remarkably convenient and doesn't require any coding skills. 

Get a free trial.

Automated E2E tests for your mobile app

Waldo provides the best-in-class runtime for all your mobile testing needs.
Get true E2E testing in minutes, not months.

Reproduce, capture, and share bugs fast!

Waldo Sessions helps mobile teams reproduce bugs, while compiling detailed bug reports in real time.