BDDfy supports asynchronous step methods using both Task-returning methods and async void methods.
The preferred approach is to return Task from your step methods:
public class AsyncScenario
{
private HttpClient _client = new();
private HttpResponseMessage _response = null!;
async Task GivenTheServiceIsRunning()
{
await _client.GetAsync("http://localhost/health");
}
async Task WhenIRequestTheResource()
{
_response = await _client.GetAsync("http://localhost/api/items");
}
async Task ThenTheResponseIsSuccessful()
{
_response.EnsureSuccessStatusCode();
}
[Fact]
public void Execute()
{
this.BDDfy();
}
}[Fact]
public void Execute()
{
this.Given(s => s.GivenTheServiceIsRunning())
.When(s => s.WhenIRequestTheResource())
.Then(s => s.ThenTheResponseIsSuccessful())
.BDDfy();
}The fluent API has dedicated overloads accepting Expression<Func<TScenario, Task>>.
BDDfy can also handle async void methods. It installs a custom SynchronizationContext to detect when the method completes:
public class AsyncVoidSteps
{
private object _sut = null!;
async void GivenSomeAsyncSetup()
{
_sut = await CreateSut();
}
void ThenBddfyHasWaitedForSetupToComplete()
{
Assert.NotNull(_sut);
}
private static async Task<object> CreateSut()
{
await Task.Delay(500);
return new object();
}
[Fact]
public void Execute()
{
this.BDDfy();
}
}Exceptions thrown in async void methods are captured and reported normally:
async void WhenSomethingFails()
{
await Task.Yield();
throw new InvalidOperationException("Something went wrong");
}BDDfy will catch this exception and mark the step as failed.
If async void detection causes issues in your environment:
Configurator.AsyncVoidSupportEnabled = false;Use Func<Task> with an explicit title:
this.Given(async () => await SetupDatabase(), "Given the database is seeded")
.When(async () => await CallApi(), "When the API is called")
.Then(async () => await VerifyResult(), "Then the result is correct")
.BDDfy();- Prefer
Task-returning methods overasync void— they're easier to debug and don't require the special synchronization context - Avoid
Task.Resultor.Wait()in step methods — useasync/awaitthroughout to prevent deadlocks - Use
async voidonly when retrofitting existing code or when the testing framework requires it