如何测试使用DispatcherTimer的类?

时间:2016-06-28 18:29:36

标签: c# wpf unit-testing dispatcher dispatchertimer

我发现了一些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;
        }
    }
}

如果我使用普通的Timer而不是使用DispatcherTimer,那么这段代码可以正常工作。但DispatcherTimer永远不会触发。我错过了什么?我需要什么来解雇它?

1 个答案:

答案 0 :(得分:4)

最好是在被测系统中避免使用DispatcherTimer并使用抽象(Rx有一个很好的称为IScheduler)。这种抽象允许您明确控制单元测试中的时间流,而不是使测试以CPU时序为条件。

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

最简单的方法是使用here中的WpfContext

[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这样的抽象实现了可靠的单元测试。