如何为使用Application.Current.Dispatcher.Invoke的方法编写WPF异步单元测试



我们有一个用WPF编写的应用程序。我正试图为一些在后台线程上运行的代码编写一个单元测试。在这段代码中的一些地方,我们需要在UI线程上做一些事情。在这些地方,我们使用以下代码结构:

Application.Current.Dispatcher.Invoke(new Action(() =>
{
// do something on UI thread
}));

当我创建一个异步单元测试时,它似乎被Invoke方法卡住了。我想这是因为调度员没有"调度"。我试图通过使用一个名为DisaptcherUtil的类来解决这个问题,该类在互联网上的许多地方都有引用。但我不能让它发挥作用。我的代码的一个简单版本现在看起来是这样的:

    [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;
        }
    }

当我运行名为"TestDispatcher"的测试时,它只是挂起。

有人知道为什么会发生这种事吗?这是正确的方法吗?还是我应该为Dispatcher创建一个接口,以便在测试中模拟它。我在一些地方见过这种做法。

我认为应该将调度隐藏在接口后面,并在单元测试中模拟它:

interface IDispatcher
{
    void Dispatch(Action action);
}

您可以在测试中轻松地模拟这一点,并期待那些分派的调用。

一个使用真实调度器并可由您的应用程序使用的实现:

public class Dispatcher : IDispatcher
{
    public void Dispatch(Action action)
    {
        Application.Current.Dispatcher.Invoke(action);
    }
}

最新更新