Actually, FA 2.0 has specific support for handling asynchronous exceptions. Just look at the unit tests in AsyncFunctionExceptionAssertionSpecs. for various examples.
Searching for nicer implementation for this unit test
-
03-04-2022 - |
Question
I use xUnit and FluentAssertions to write my unit tests and I am stuck at the following problem. As I have not implemented the catch
(in GetCountriesAsync
) of the WebException
yet, I throw a new NotImplementedException
in this place.
This code is the only way I made the test actually work as expected. I added the native xUnit implementation either, due to FluentAssertions is just syntactic sugar.
[Fact]
public async Task GetCountriesAsyncThrowsExceptionWithoutInternetConnection()
{
// Arrange
Helpers.Disconnect(); // simulates network disconnect
var provider = new CountryProvider();
try
{
// Act
var countries = await provider.GetCountriesAsync();
}
catch (Exception e)
{
// Assert FluentAssertions
e.Should().BeOfType<NotImplementedException>();
// Assert XUnit
Assert.IsType<NotImplementedException>(e);
}
}
Though I found this implementation a lot nicer, it just doesn't work.
[Fact]
public async Task GetCountriesAsyncThrowsExceptionWithoutInternetConnection3()
{
// Arrange
Helpers.Disconnect(); // simulates network disconnect
var provider = new CountryProvider();
// Act / Assert FluentAssertions
provider.Invoking(async p => await p.GetCountriesAsync())
.ShouldThrow<NotImplementedException>();
// Act / Assert XUnit
Assert.Throws<NotImplementedException>(async () => await provider.GetCountriesAsync());
}
As VS2012/ReSharper already suggests to remove the redundant async
keyword of the test method, I replaced async Task
with void
and the test still behaves the same, so I suspect the async Action
s cannot be awaited, they're rather fired and forgotten.
Is there a way to implement this properly with xUnit/FluentAssertions? I think I have to go with my first implementation as I can't see any functionality like InvokingAsync()
.
Solution
OTHER TIPS
Regarding FluentAssertions, I've added the following to my code:
using System;
using System.Threading.Tasks;
namespace FluentAssertions
{
public static class FluentInvocationAssertionExtensions
{
public static Func<Task> Awaiting<T>(this T subject, Func<T, Task> action)
{
return () => action(subject);
}
}
}
and now you can do:
_testee.Awaiting(async x => await x.Wait<Foo>(TimeSpan.Zero))
.ShouldThrow<BarException>();
whereas _teste.Wait<T>
returns a Task<T>
.
Also the naming of the method Awaiting
make sense, because pure invocation of the method will not result in the exception being caught by the caller, you do need to use await to do so.