We have an application written in WPF. I'm trying to write a unit test for some code that runs on background threads. On a few place in this code we need to do things on the UI thread. In those places we use the following code structure:
Application.Current.Dispatcher.Invoke(new Action(() =>
{
// do something on UI thread
}));
When I create an async unit test it seems to get stuck on the Invoke method. I guess this is because the dispatcher is not "dispatching". I've tried to fix this by using a class called DisaptcherUtil that is referenced in a number of places on the internet. But I can't get this to work. A simplistic version of my code now looks like this:
[TestMethod]
public async Task TestDispatcher()
{
new Application();
DispatcherUtil.DoEvents();
await Task.Run(() => MethodUsingDispatcher());
}
private void MethodUsingDispatcher()
{
Application.Current.Dispatcher.Invoke(new Action(() =>
{
Console.WriteLine("On the dispatchee thread!");
}));
Console.WriteLine("BAck to background trhead");
}
public static class DispatcherUtil
{
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
private static object ExitFrame(object frame)
{
Console.WriteLine("ExitFrame");
((DispatcherFrame)frame).Continue = false;
return null;
}
}
When I run the test called "TestDispatcher" it just hangs.
Anyone have any ideas why this is happening? Is this the right way to do this or should I instead go down the route of creating an interface for the Dispatcher that I could mock in the tests. I've seen this done in some places.
I'd say that you should hide the dispatching behind an interface and mock it in the unit tests:
interface IDispatcher
{
void Dispatch(Action action);
}
You can easily mock this in your tests and expect to those dispatched calls.
An implementation which uses the real dispatcher and can be used by your app:
public class Dispatcher : IDispatcher
{
public void Dispatch(Action action)
{
Application.Current.Dispatcher.Invoke(action);
}
}