Search code examples
c#wpfmultithreadingmstestdispatcher

How to write a WPF async unit test for a method that uses Application.Current.Dispatcher.Invoke?


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.


Solution

  • 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);
        }
    }