βœ… Async void method testing in xUnit

In WinForms, it's required to use async void for event handlers like button clicks due to its synchronous nature. However, when trying to test an event handler like the one below, how can I indirectly "await" async void for proper method execution. Async void method:
c#
internal async void HandleSubmit_Clicked(object? sender, EventArgs e)
{
try
{
bool Valid = await ValidFormAsync();
if (Valid)
{
_dataForm.OnSubmissionComplete(this, new SubmissionCompletedEventArgs(_dataForm.GetData(), _dataForm.Mode));
SubmissionCompleted?.Invoke(this, new SubmissionCompletedEventArgs(_dataForm.GetData(), _dataForm.Mode));
}
}
catch (Exception ex)
{
_logger.LogError("Submission Failed due to: {Exception}", ex.Message);
_dataForm.ShowMessageBox("Submission Failed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

protected virtual async Task<bool> ValidFormAsync()
{
_logger.LogCritical("ValidFormAsync is running in parent. Child must override it.");
await Task.Delay(100);
return true;
}
c#
internal async void HandleSubmit_Clicked(object? sender, EventArgs e)
{
try
{
bool Valid = await ValidFormAsync();
if (Valid)
{
_dataForm.OnSubmissionComplete(this, new SubmissionCompletedEventArgs(_dataForm.GetData(), _dataForm.Mode));
SubmissionCompleted?.Invoke(this, new SubmissionCompletedEventArgs(_dataForm.GetData(), _dataForm.Mode));
}
}
catch (Exception ex)
{
_logger.LogError("Submission Failed due to: {Exception}", ex.Message);
_dataForm.ShowMessageBox("Submission Failed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

protected virtual async Task<bool> ValidFormAsync()
{
_logger.LogCritical("ValidFormAsync is running in parent. Child must override it.");
await Task.Delay(100);
return true;
}
The test:
c#
[Fact]
public void HandleSubmit_Clicked_RaisesSubmissionCompletedEvent()
{
// Arrange
_presenterTemplate = new DataFormPresenterTemplate(_dataFormTemplate, _testLogger);

bool eventRaised = false;
void eventHandler(object? sender, SubmissionCompletedEventArgs args)
{
eventRaised = true;
}
_presenterTemplate.SubmissionCompleted += eventHandler;

// Act
_presenterTemplate.HandleSubmit_Clicked(null, EventArgs.Empty);

// Assert
Assert.True(eventRaised);

// Cleanup
_presenterTemplate.SubmissionCompleted -= eventHandler;
}
c#
[Fact]
public void HandleSubmit_Clicked_RaisesSubmissionCompletedEvent()
{
// Arrange
_presenterTemplate = new DataFormPresenterTemplate(_dataFormTemplate, _testLogger);

bool eventRaised = false;
void eventHandler(object? sender, SubmissionCompletedEventArgs args)
{
eventRaised = true;
}
_presenterTemplate.SubmissionCompleted += eventHandler;

// Act
_presenterTemplate.HandleSubmit_Clicked(null, EventArgs.Empty);

// Assert
Assert.True(eventRaised);

// Cleanup
_presenterTemplate.SubmissionCompleted -= eventHandler;
}
Any ideas on how to solve this?
15 Replies
Mayor McCheese
Mayor McCheeseβ€’4w ago
Is just deleting this whole file an option?
[-InsertNameHere-]
[-InsertNameHere-]OPβ€’4w ago
I wish. Unfortunately there is alot of places in winforms where i will need to deal with async void since there is buttons everywhere that i need to test
Mayor McCheese
Mayor McCheeseβ€’4w ago
What are you really testing?
[-InsertNameHere-]
[-InsertNameHere-]OPβ€’4w ago
in this particular instance, its just that the event is properly raised. If i remove await Task.Delay(100) from ValidFormAsync - it runs just fine but it hangs once i re-add it.
Mayor McCheese
Mayor McCheeseβ€’4w ago
What if you don't test it at all?
[-InsertNameHere-]
[-InsertNameHere-]OPβ€’4w ago
I mean, i could but this feels like leaving the problem to the future. I assume there is atleast one case you would need to test async void? Or is that just a sign of bad code then?
Mayor McCheese
Mayor McCheeseβ€’4w ago
What problem do you expect to have happen in the future?
[-InsertNameHere-]
[-InsertNameHere-]OPβ€’4w ago
As of now, none. Im just assuming there would be a situation somewhere and i havent seen it yet
Mayor McCheese
Mayor McCheeseβ€’4w ago
I'm reading on my phone, but this seems like a bit of overkill
[-InsertNameHere-]
[-InsertNameHere-]OPβ€’4w ago
Could be, all good then. Ill look at how to re-design my code so the important parts are testable. Regardless, thanks for the assistance
Mayor McCheese
Mayor McCheeseβ€’4w ago
Ideally the important parts aren't too tightly bound with winforms code I feel like you're testing that when you press the button the button was pressed
[-InsertNameHere-]
[-InsertNameHere-]OPβ€’4w ago
It isnt what im testing but i get how it could look like that. So my codebase currently follows the MVP pattern. When Submit is clicked in the view, an event is invoked that is then subscribed to and handled by HandleSubmit_Clicked. I can just extract the code like this:
c#
internal async void HandleSubmit_Clicked(object? sender, EventArgs e)
{
await HandleSubmit_ClickedAsync(sender, e);
}

internal async Task HandleSubmit_ClickedAsync(object? sender, EventArgs e)
{
try
{
bool Valid = await ValidFormAsync();
if (Valid)
{
_logger.LogInformation("Is valid");
_dataForm.OnSubmissionComplete(this, new SubmissionCompletedEventArgs(_dataForm.GetData(), _dataForm.Mode));
SubmissionCompleted?.Invoke(this, new SubmissionCompletedEventArgs(_dataForm.GetData(), _dataForm.Mode));
}
else
{
_logger.LogInformation("Is not valid");
}
}
catch (Exception ex)
{
_logger.LogError("Submission Failed due to: {Exception}", ex.Message);
_dataForm.ShowMessageBox("Submission Failed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
c#
internal async void HandleSubmit_Clicked(object? sender, EventArgs e)
{
await HandleSubmit_ClickedAsync(sender, e);
}

internal async Task HandleSubmit_ClickedAsync(object? sender, EventArgs e)
{
try
{
bool Valid = await ValidFormAsync();
if (Valid)
{
_logger.LogInformation("Is valid");
_dataForm.OnSubmissionComplete(this, new SubmissionCompletedEventArgs(_dataForm.GetData(), _dataForm.Mode));
SubmissionCompleted?.Invoke(this, new SubmissionCompletedEventArgs(_dataForm.GetData(), _dataForm.Mode));
}
else
{
_logger.LogInformation("Is not valid");
}
}
catch (Exception ex)
{
_logger.LogError("Submission Failed due to: {Exception}", ex.Message);
_dataForm.ShowMessageBox("Submission Failed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
the async Task should be testable but it looks messy. If i try to make the handleSubmit itself async Task, i get the error "'Task DataFormPresenterTemplate.HandleSubmit_Clicked(object?, EventArgs)' has the wrong return type". Hence why i want to do a bit of research. I should be good on my own though. Thank you once again for your input and hope you have a great day
Unknown User
Unknown Userβ€’4w ago
Message Not Public
Sign In & Join Server To View
MODiX
MODiXβ€’4w ago
If you have no further questions, please use /close to mark the forum thread as answered
[-InsertNameHere-]
[-InsertNameHere-]OPβ€’4w ago
Thanks for the additional info. Will definitely look into itπŸ‘

Did you find this page helpful?