在单元测试中使用WPF Dispatcher的正确方法

问题描述 投票:7回答:2

我试图遵循Using the WPF Dispatcher in unit tests的建议,以便让我的nUnit测试运行。

当我按如下所示编写单元测试时,它可以工作:

[Test]
public void Data_Should_Contain_Items()
{
    DispatcherFrame frame = new DispatcherFrame();
        PropertyChangedEventHandler waitForModelHandler = delegate(object sender, PropertyChangedEventArgs e)
        {
          if (e.PropertyName == "Data")
          {
            frame.Continue = false;
          }
        };
    _myViewModel.PropertyChanged += waitForModelHandler;
    Dispatcher.PushFrame(frame);

    Assert.IsTrue(_myViewModel.Data.Count > 0, "Data item counts do not match");
}

但是,如果我尝试使用DispatcherUtil的建议,它不起作用:

[Test]
public void Data_Should_Contain_Items()
{
    DispatcherUtil.DoEvents();
    Assert.IsTrue(_myViewModel.Data.Count > 0, "Data item counts do not match");
}

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)
    {
        ((DispatcherFrame)frame).Continue = false;
        return null;
    }
}

当我使用DispatcherUtil时,看起来在数据准备好之前调用ExitFrame太快了。

我没有正确使用DispatcherUtil吗?这似乎是一种更好的方法,用于处理调度程序,而不是等待视图模型的回调。

c# wpf unit-testing nunit dispatcher
2个回答
7
投票

由于调度程序在单元测试中存在问题,我的解决方案是破坏视图模型对调度程序的依赖性。我假设你目前有硬编码的引用,如:

Dispatcher.CurrentDispatcher.BeginInvoke(..

调度程序是外部依赖项,不应该是单元测试的一部分 - 我们可以假设调度程序工作。

我会使用依赖注入(穷人,Unity等)。创建一个代表调度程序的合适接口。创建一个真正的实现,包装真正的调度程序。创建一个使用Action.BeginInvoke的虚假实现。在伪造中,您记录返回到BeginInvoke调用的所有IAsyncResults。 然后有一个帮助方法,等待所有完成的调用,您可以在测试中使用等待完成。

或者有一个视图模型基类,它做同样的事情。正常调用调度程序,但可以在测试期间指示调用伪造。


2
投票

我找到了一个简单的解决方我没有在Dispatcher中处理Frames Async,而是在DispatcherUtil类中添加了一个同步方法。调用此DoEventsSync() - 方法在处理所有帧时返回,我认为这应该有帮助:

    public static class DispatcherUtil
    {
        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        public static void DoEvents()
        {
            var frame = new DispatcherFrame();
            Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
                new DispatcherOperationCallback(ExitFrame), frame);
            Dispatcher.PushFrame(frame);
        }

        public static void DoEventsSync()
        {
            var frame = new DispatcherFrame();
            Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background,
                new DispatcherOperationCallback(ExitFrame), frame);
            Dispatcher.PushFrame(frame);
        }

        private static object ExitFrame(object frame)
        {
            ((DispatcherFrame)frame).Continue = false;
            return null;
        }
    }

现在只需在单元测试中使用DispatcherUtil.DoEventsSync();而不是DispatcherUtil.DoEvents();。在单元测试继续之前,您可以确保Dispatcher处理了所有内容。不需要为测试添加回调。

令人困惑的部分是DispatcherUtil.DoEvents();真的是DispatcherUtil.DoEventsAsync();因为BeginInvoke(..)是异步方法

© www.soinside.com 2019 - 2024. All rights reserved.