95

I'm a bit stuck with this code (this is a sample):

public async Task Fail()
{
    await Task.Run(() => { throw new Exception(); });
}

[Test]
public async Task TestFail()
{
    Action a = async () => { await Fail(); };
    a.ShouldThrow<Exception>();
}

The code doesn't catch the exception, and fails with

Expected a System.Exception to be thrown, but no exception was thrown.

I'm sure I'm missing something, but docs seem to suggest this is the way to go. Some help would be appreciated.

Eugene
  • 1,073
  • 1
  • 7
  • 7
  • Yes, thank you. Missed. Doesn't fix the issue, though. – Eugene Mar 14 '17 at 15:29
  • 1
    what is the **issue**? – Selman Genç Mar 14 '17 at 15:30
  • @stuartd Actually, no, async is req'd when testing async methods. ShouldThrow is not async. Could that be a reason? – Eugene Mar 14 '17 at 15:35
  • @user227895 this works for me: `Assert.CatchAsync(async () => await Fail());` – Selman Genç Mar 14 '17 at 15:37
  • 2
    Your problem is `Action` - this creates an `async void` method. The proper asynchronous delegate equivalent is `Func`. – Stephen Cleary Mar 14 '17 at 15:39
  • 1
    There's no reason for `Fail` to call `Task.Run`, it can just throw an exception, and there's no reason for `TestFail` to have an anonymous method, you can just use `Fail` for the delegate without wrapping it in another method call that doesn't do anything. – Servy Mar 14 '17 at 15:51

5 Answers5

140

You should use Func<Task> instead of Action:

[Test]
public void TestFail()
{
    Func<Task> f = async () => { await Fail(); };
    f.ShouldThrow<Exception>();            
}

That will call the following extension which is used to verify asynchronous methods

public static ExceptionAssertions<TException> ShouldThrow<TException>(
    this Func<Task> asyncAction, string because = "", params object[] becauseArgs)
        where TException : Exception        

Internally this method will run task returned by Func and wait for it. Something like

try
{
    Task.Run(asyncAction).Wait();
}
catch (Exception exception)
{
    // get actual exception if it wrapped in AggregateException
}

Note that test itself is synchronous.

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • 5
    Is the async await really necessary? Seems to work fine with just `Func f = () => Fail();` – Geir Sagberg Nov 05 '17 at 09:33
  • 2
    It is if you are interested in waiting for the call to finish execution before the test does. – Elan Hasson Mar 14 '18 at 17:51
  • 20
    FYI - in the latest version (v5) of fluentassertions the syntax is: f.Should().Throw – S. Rasmussen Apr 30 '18 at 14:43
  • If the `ShouldThrow` method awaits the resultant task it shouldnt matter if you await it in the func should it? – Marie May 07 '18 at 18:18
  • Don't know if it's new or it was available back then already, but [official website](https://fluentassertions.com/exceptions/) says that action can by used as well. Even though I couldn't make it work for some reason. – Constantine Ketskalo Dec 24 '19 at 15:29
45

With Fluent Assertions v5+ the code will be like :

ISubject sut = BuildSut();
//Act and Assert
Func<Task> sutMethod = async () => { await sut.SutMethod("whatEverArgument"); };
await sutMethod.Should().ThrowAsync<Exception>();

This should work.

Károly Ozsvárt
  • 1,025
  • 1
  • 12
  • 24
gatsby
  • 1,148
  • 11
  • 12
  • 4
    Is there not an await missing? `await sutMethod.Should().ThrowAsync();` – Jehof May 10 '19 at 09:47
  • 3
    I think in the 3rd row, the async-await keyword-pair is unnecessary, it works for me without it as well. – Károly Ozsvárt Sep 04 '19 at 11:22
  • 1
    @Károly Ozsvárt : I think it works for you since the code you are testing does not have a an await that requires continuation before hitting the expected throw. This will be common for validation code in a method (since it s usual first) and might be common in a test since all dependencies are probably mocked to return completed tasks. But, it won't work if you hit a continuation. – steve Sep 18 '20 at 20:00
  • The synchronous version `sutMethod.Should().Throw();` worked for me as well. I think this is just blocking and I'm fine with this being a blocking call in my test. – linbianxiaocao Dec 11 '20 at 20:15
  • 1
    How do you test message? await sutMethod.Should().ThrowAsync().WithMessage("AlaMaKota"); ? – Leszek P May 06 '21 at 08:18
4

Other variation of usage ThrowAsync method:

await Should.ThrowAsync<Exception>(async () => await Fail());
Bronek
  • 10,722
  • 2
  • 45
  • 46
4

With Fluent Assertions v5.7 they introduced the Awaiting overload so now you can do as following:

public async void TestFail()
{
    await this.Awaiting(_ => Fail()).Should().ThrowAsync<Exception>();
}
Serhii
  • 723
  • 1
  • 4
  • 12
enzo-ae
  • 41
  • 2
0

With Fluent Assertions v6.9 you can use short form:

var act = () => Fail();
act.Should().ThrowAsync<Exception>();