Testing for Thrown Exceptions in xUnit.net

When writing tests it is sometimes useful to check that the correct exceptions are thrown at the expected time.

When using xUnit.net there are a number of ways to accomplish this.

As an example consider the following simple class:

public class TemperatureSensor
{
    bool _isInitialized;

    public void Initialize()
    {
        // Initialize hardware interface
        _isInitialized = true;
    }

    public int ReadCurrentTemperature()
    {
        if (!_isInitialized)
        {
            throw new InvalidOperationException("Cannot read temperature before initializing.");
        }

        // Read hardware temp
        return 42; // Simulate for demo code purposes
    }        
}

The first test we could write against the preceding class is to check the “happy path”:

[Fact]
public void ReadTemperature()
{
    var sut = new TemperatureSensor();

    sut.Initialize();

    var temperature = sut.ReadCurrentTemperature();

    Assert.Equal(42, temperature);
}

Next a test could be written to check that if the temperature is read before initializing the sensor, an exception of type InvalidOperationException is thrown. To do this the xUnit.net Assert.Throws method can be used. When using this method the generic type parameter indicates the type of expected exception and the method parameter takes an action that should cause this exception to be thrown, for example:

[Fact]
public void ErrorIfReadingBeforeInitialized()
{
    var sut = new TemperatureSensor();

    Assert.Throws<InvalidOperationException>(() => sut.ReadCurrentTemperature());
}

In the preceding test, if an InvalidOperationException is not thrown when the ReadCurrentTemperature method is called the test will fail.

The thrown exception can also be captured in a variable to make further asserts against the exception property values, for example:

[Fact]
public void ErrorIfReadingBeforeInitialized_CaptureExDemo()
{
    var sut = new TemperatureSensor();

    var ex = Assert.Throws<InvalidOperationException>(() => sut.ReadCurrentTemperature());

    Assert.Equal("Cannot read temperature before initializing.", ex.Message);
}

The Assert.Throws method expects the exact type of exception and not derived exceptions. In the case where you want to also allow derived exceptions, the Assert.ThrowsAny method can be used.

Similar exception testing features also exist in MSTest and NUnit frameworks.

To learn more about using exceptions to handle errors in C#, check out my Error Handling in C# with Exceptions Pluralsight course.You can start watching with a Pluralsight free trial.

SHARE:

Comments (5) -

  • Sudhanshu

    8/23/2018 2:34:53 AM | Reply

    Hi  Jason,
    Thanks for sharing. I tend to use the Record class to structure my xUnit tests that verify exception scenarios:
    [Fact]
    public void InsertTestNameHere()
    {  
              var input = "a string";  
              var exception = Record.Exception(() => int.Parse(input));    
             Assert.NotNull(exception);    Assert.IsType<FormatException>(exception);
    }

    • Dominique Louis

      5/13/2020 1:11:15 PM | Reply

      @Sudhanshu  Does using Record.Exception still work for you? I'm using the latest dotnet and trying to catch an EntryPointNotFoundException and it basically crashes the whole unit test system. Anyone else seeing this?

      • Gardo

        5/27/2020 2:14:26 PM | Reply

        It crashes for me too suddenly

  • Jonathan

    12/3/2019 11:43:19 PM | Reply

    Sudhanshu, I just discovered that you can keep the exception too!
    |        ...
    |        var except1 = Assert.Throws<ArgumentOutOfRangeException>(() => array[999, 999] = 123);
    |        ...

  • koyal

    6/9/2020 12:28:34 PM | Reply

    Hi Jason, I like your tuorials so much of X.unit testing  but one questions here can we mock  static classes and static methods so I need to know how  can we do this things without changing so much dependecy classes based on static classes as well as static methods
    Can you please make the video mocking static classes and methods

Pingbacks and trackbacks (1)+

Add comment

Loading