【问题标题】:How can I test a class that uses DispatcherTimer?如何测试使用 DispatcherTimer 的类?
【发布时间】:2016-06-28 18:29:36
【问题描述】:

我发现了几个 Stack Overflow 问题以及一些已经涉及该主题的博客文章,但不幸的是,它们都不能满足我的需求。我将从一些示例代码开始,以展示我想要完成的工作。

using System;
using System.Security.Permissions;
using System.Threading.Tasks;
using System.Windows.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace MyApp
{
    [TestClass]
    public class MyTests
    {
        private int _value;

        [TestMethod]
        public async Task TimerTest()
        {
            _value = 0;
            var timer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(10)};
            timer.Tick += IncrementValue;
            timer.Start();

            await Task.Delay(15);
            DispatcherUtils.DoEvents();
            Assert.AreNotEqual(0, _value);
        }

        private void IncrementValue(object sender, EventArgs e)
        {
            _value++;
        } 
    }

    internal class DispatcherUtils
    {
        [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);
        }

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

如果我不使用 DispatcherTimer,而是使用普通 Timer,则此代码可以正常工作。但是 DispatcherTimer 从不触发。我错过了什么?我需要什么才能让它着火?

【问题讨论】:

  • 我认为您需要将 SynchronizationContext 设置为 DispatcherSynchronizationContext 的一个实例。否则,在等待的另一端,您将处于一个新线程上,该线程将有一个新的调度程序,这不是您要为其处理事件的那个。

标签: c# wpf unit-testing dispatcher dispatchertimer


【解决方案1】:

如果您可以在您的被测系统中避免使用DispatcherTimer 并改用抽象(Rx 有一个很好的称为IScheduler),那将是最好的。这种抽象允许您在单元测试中显式控制时间流,而不是让您的测试以 CPU 计时为条件。

但如果您现在只对单元测试感兴趣,那么您需要创建一个执行消息泵送的 STA 线程并且安装正确的Dispatcher。所有“在调度程序上运行此代码”操作只需将委托包装在 Win32 消息中,如果您没有 Win32 消息泵循环in Dispatcher之前 em> 创建计时器),那么这些消息将不会被处理。

最简单的方法是使用来自hereWpfContext

[TestMethod]
public async Task TimerTest()
{
  await WpfContext.Run(() =>
  {
    _value = 0;
    var timer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(10)};
    timer.Tick += IncrementValue;
    timer.Start();

    await Task.Delay(15);
    Assert.AreNotEqual(0, _value);
  });
}

同样,这种方法不合标准,因为它取决于时间安排。因此,如果您的防病毒软件感到不安并决定检查您的单元测试,它可能会虚假地失败。像 IScheduler 这样的抽象可以实现可靠的单元测试。

【讨论】:

  • 优秀的答案。非常感谢您添加的所有细节。
  • 我不得不采用这种方法,因为我在一个庞大的遗留代码库中工作,重构意味着巨大的回归风险。
猜你喜欢
  • 1970-01-01
  • 2020-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多