【问题标题】:Correct method for using the WPF Dispatcher in unit tests在单元测试中使用 WPF 调度程序的正确方法
【发布时间】:2012-02-17 22:10:48
【问题描述】:

我正在尝试遵循 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 吗?这似乎是一种更好的方法来处理调度程序,而不是等待来自视图模型的回调。

【问题讨论】:

  • 如果 PropertyChangedEventHandler 为属性“数据”调用,您要测试什么?如果是这样,为什么需要调度员参与?我也没有使用 _myViewModel 来附加处理程序。
  • @Phil:当 _myViewModel 被实例化时,它的构造函数会进行异步调用。当该调用完成时,_myViewModel.Data 应该有一些值。我正在尝试测试 Data 实际上是否已填充,但是由于 asyn 调用而填充 Data 的事实给我带来了一些麻烦。我想避免在可能必须处理 Dispatcher 的任何单元测试中监听 PropertyChanged 事件。

标签: c# wpf unit-testing nunit dispatcher


【解决方案1】:

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

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(..) 是一个异步方法

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-26
    • 1970-01-01
    • 2016-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    相关资源
    最近更新 更多