How to Test Expected Exceptions in NUnit and xUnit
Testing for expected exceptions is a critical aspect of unit testing in C# to ensure your code fails gracefully and handles invalid inputs correctly. Both NUnit and xUnit provide built-in assertions designed to catch thrown exceptions, inspect their types, and evaluate their error messages. This guide covers how to implement synchronous and asynchronous exception tests in both frameworks using clean, standard syntax.
Testing Exceptions in NUnit
NUnit utilizes the Assert.Throws<T> method to
verify that a specific delegate throws the expected exception type.
Synchronous Exceptions
To test a synchronous method, pass the executing code inside an
Action delegate or lambda expression.
Assert.Throws<T> returns the caught exception,
allowing you to run further assertions on properties such as the error
message.
using NUnit.Framework;
using System;
[TestFixture]
public class NUnitExceptionTests
{
[Test]
public void Divide_ByZero_ThrowsDivideByZeroException()
{
var calculator = new Calculator();
var exception = Assert.Throws<DivideByZeroException>(() => calculator.Divide(10, 0));
Assert.That(exception.Message, Is.EqualTo("Cannot divide by zero."));
}
}Alternatively, you can use the constraint-based model with
Assert.That:
[Test]
public void Divide_ByZero_ThrowsDivideByZeroException_ConstraintModel()
{
var calculator = new Calculator();
Assert.That(() => calculator.Divide(10, 0), Throws.TypeOf<DivideByZeroException>());
}Asynchronous Exceptions
For code that returns a Task, use
Assert.ThrowsAsync<T>:
[Test]
public void FetchDataAsync_InvalidId_ThrowsArgumentException()
{
var service = new DataService();
var exception = Assert.ThrowsAsync<ArgumentException>(async () => await service.FetchDataAsync(-1));
Assert.That(exception.ParamName, Is.EqualTo("id"));
}Testing Exceptions in xUnit
xUnit also relies on an Assert.Throws<T> pattern,
which is strictly generic and functional.
Synchronous Exceptions
In xUnit, Assert.Throws<T> intercepts the
exception and returns the instance for message and property
validation:
using System;
using Xunit;
public class XunitExceptionTests
{
[Fact]
public void Divide_ByZero_ThrowsDivideByZeroException()
{
var calculator = new Calculator();
var exception = Assert.Throws<DivideByZeroException>(() => calculator.Divide(10, 0));
Assert.Equal("Cannot divide by zero.", exception.Message);
}
}Asynchronous Exceptions
For asynchronous operations, xUnit provides
Assert.ThrowsAsync<T>. Unlike NUnit,
Assert.ThrowsAsync<T> returns a
Task<TException>, so the test method must be marked
async and the assertion itself must be awaited:
[Fact]
public async Task FetchDataAsync_InvalidId_ThrowsArgumentException()
{
var service = new DataService();
var exception = await Assert.ThrowsAsync<ArgumentException>(() => service.FetchDataAsync(-1));
Assert.Equal("id", exception.ParamName);
}Summary of Differences
- Exact Types vs. Derived Types: Both
Assert.Throws<T>in NUnit and xUnit match exact exception types. If you need to allow derived exception types, NUnit offersThrows.InstanceOf<T>(), while xUnit providesAssert.ThrowsAny<T>()andAssert.ThrowsAnyAsync<T>(). - Async Awaiting: In xUnit, you
await Assert.ThrowsAsync<T>(). In NUnit,Assert.ThrowsAsync<T>()handles the task execution internally and returns the exception synchronously.