In the past, within my team at Innoveo, we had several discussions about the best way to unit test async WPF ICommand. We value quality, so testing is essential to us. We decided to make the methods called by the command
internal so that our tests could call those.
What is the problem with unit testing an Async WPF ICommand? The problem is that the command is an
async void method! So, you have no way to
await the end of the execution of your command. So, your test might assert on things that are still executing.
Some weeks ago, a question about “Extract and Override call” taken from the book “Working Effectively with Legacy Code” for testing was raised on the French DevApps community. I immediately thought of the discussions about testing async WPF ICommand and proposed that solution as a pragmatic solution that is not only easy to implement but also very simple to understand. Some did not like that solution because it was not perfect. I agree, but sometimes it is good to be pragmatic.
The discussion went further about
async void testing and Gérald Barré from Meziantou’s blog, which I highly recommend, answered it interestingly! He made a blog post about his solution “Awaiting an async void method in .NET“ which leverage his own implementation of a SynchronizationContext and
I decided to apply his solution to our original question; “How to test an async WPF ICommand”?
The first one fails because nothing awaits the execution of the WPF command. The second one succeeds because the test is awaiting the command execution. Nice, very nice!
And here is the code of the
public class MainWindowViewModel : ObservableObject
So, that is one way of solving testing an async WPF ICommand. But it is not the only way.
Windows Community Toolkit MVVM provides the
AsyncRelayCommand which implements
IAsyncRelayCommand providing an
ExecuteAsync method returning a
Task which can be awaited.
And in this case we need to declare the command as an
private IAsyncRelayCommand? _otherClick;
and use the
ExecuteAsync method in our test
As a developer, I think it is always good to be pragmatic but this does not avoid to search for better solutions.