Skip to content

Exceptions

There are three ways to assert on exceptions, depending on whether the code under test is synchronous or asynchronous, and whether you already have an exception instance.

Actions (Synchronous)

Call .Should() on an Action to get ActionAssertions:

Action throws = () => throw new InvalidOperationException("oops");

throws.Should().Throw<InvalidOperationException>();

Assert that no exception is thrown:

Action safe = () => { /* ... */ };
safe.Should().NotThrow();

Asserting the Message

throws.Should().Throw<InvalidOperationException>("oops");

Asserting Message and Inner Exception

var inner = new Exception("inner");
Action nested = () => throw new InvalidOperationException("outer", inner);

nested.Should().Throw<InvalidOperationException>("outer", inner);

Asserting a Specific Exception Instance

var expected = new InvalidOperationException("oops");
Action throws = () => throw expected;

throws.Should().Throw(expected);

ArgumentException Shorthand

Action badArg = () => throw new ArgumentException("must be positive", "count");

badArg.Should().ThrowArgumentException("must be positive", "count");

See ThrowArgumentException.

ArgumentOutOfRangeException Shorthand

Action outOfRange = () => throw new ArgumentOutOfRangeException("count", -1, "must be positive");

outOfRange.Should().ThrowArgumentOutOfRangeException("must be positive", "count", -1);

See ThrowArgumentOutOfRangeException.

Async Actions

Call .Should() on a Func<Task> to get AsyncActionAssertions. All methods return Task and should be awaited:

Func<Task> throwsAsync = async () =>
{
    await Task.Delay(1);
    throw new InvalidOperationException("oops");
};

await throwsAsync.Should().ThrowAsync<InvalidOperationException>();
await throwsAsync.Should().ThrowAsync<InvalidOperationException>("oops");
await throwsAsync.Should().ThrowArgumentExceptionAsync("bad arg", "paramName");
await throwsAsync.Should().ThrowArgumentOutOfRangeExceptionAsync("out of range", "paramName", -1);

Func<Task> safeAsync = async () => await Task.Delay(1);
await safeAsync.Should().NotThrowAsync();

The Invoking / Awaiting Pattern

Use Invoking and Awaiting extension methods from InvokingExtensions to test methods on an object without creating a separate Action variable:

var parser = new Parser();

// Synchronous
parser.Invoking(p => p.Parse(null!))
    .Should().ThrowArgumentException("Value cannot be null", "input");

// Async
await parser.Awaiting(p => p.ParseAsync(null!))
    .Should().ThrowArgumentExceptionAsync("Value cannot be null", "input");

If the method returns a value, use the overloads that discard the result:

parser.Invoking(p => p.TryParse(null!, out _))
    .Should().NotThrow();

Asserting on an Exception Directly

Call .Should() on an Exception instance to get ExceptionAssertions<T>:

var exception = new InvalidOperationException("oops");

exception.Should().HaveMessage("oops");
exception.Should().NotHaveMessage("something else");
exception.Should().HaveMessageStartingWith("oo");

Inner Exceptions

var inner = new ArgumentException("inner");
var outer = new InvalidOperationException("outer", inner);

outer.Should().HaveInnerException<ArgumentException>().And.HaveMessage("inner");

// Assert a specific instance
outer.Should().HaveInnerException(inner);

// Assert no inner exception
new Exception("no inner").Should().NotHaveInnerException();

See HaveInnerException and NotHaveInnerException.

Chaining After Throw

Throw<T> and ThrowAsync<T> return an ActionAssertionsChain<TException> with .Exception and .That properties for further assertions:

var chain = throws.Should().Throw<ArgumentException>();

// .Exception gives the thrown exception
chain.Exception.Should().HaveMessage("bad argument");

// .That gives ExceptionAssertions<T> directly for chaining
throws.Should().Throw<ArgumentException>()
    .And.HaveMessage("bad argument")
    .And.HaveParamName("value");