【问题标题】:NMock issue testing against WPF and Dispatcher针对 WPF 和 Dispatcher 的 NMock 问题测试
【发布时间】:2011-01-06 21:46:42
【问题描述】:

这里有一个线程迷。我有这个方法:

    public void RefreshMelts()
    {
        MeltsAvailable.Clear();

        ThreadPool.QueueUserWorkItem(delegate
        {
            Dispatcher.BeginInvoke((ThreadStart)delegate
            {
                eventAggregator.GetEvent<BusyEvent>().Publish(true);
                eventAggregator.GetEvent<StatusMessageEvent>().Publish(
                    new StatusMessage("Loading melts...", MessageSeverity.Low));
            });

            try
            {
                IList<MeltDto> meltDtos = meltingAppService.GetActiveMelts();

                Dispatcher.Invoke((ThreadStart)delegate
                {
                    foreach (MeltDto availableMelt in meltDtos)
                    {
                        MeltsAvailable.Add(availableMelt);
                    }
                    OnPropertyChanged("MeltsAvailable");

                    eventAggregator.GetEvent<BusyEvent>().Publish(false);
                    eventAggregator.GetEvent<StatusMessageEvent>().Publish(
                        new StatusMessage("Melts loaded", MessageSeverity.Low));
                });
            }
            catch (ApplicationException ex)
            {
                log.Error("An error occurred in MeltsViewModel when attempting to load melts", ex);

                Dispatcher.Invoke((ThreadStart)delegate
                {
                    MeltsAvailable.Clear();

                    eventAggregator.GetEvent<StatusMessageEvent>().Publish(
                        new StatusMessage("Melt data could not be loaded because an error occurred; " +
                            "see the application log for detail",
                            MessageSeverity.High));
                    eventAggregator.GetEvent<BusyEvent>().Publish(false);
                });
            }

        });

    }

这是在 WPF 用户控件中定义的。 MeltsAvailable 是 MeltDtos 的 ObservableCollection。此代码在应用程序本身中运行时运行良好。

问题是我想创建一个单元测试,使用 NMock 来验证这个方法的结果 - 具体来说,一旦它被调用, MeltsAvailable 属性就会有一些项目。测试方法如下:

    [TestMethod]
    public void GetAvailableMeltsTest()
    {
        MeltDto mockMelt1 = new MeltDto();
        MeltDto mockMelt2 = new MeltDto();

        mockMelt1.MeltIdentifier = "TST0001";
        mockMelt2.MeltIdentifier = "TST0002";

        IList<MeltDto> availableMelts = new List<MeltDto>();
        availableMelts.Add(mockMelt1);
        availableMelts.Add(mockMelt2);

        Expect.Exactly(1).On(service).Method("GetActiveMelts").Will(Return.Value(availableMelts));


        MeltsViewModel vm = new MeltsViewModel(aggregator, logger, service, configManagerFactory); // All of these are mock objects

        vm.RefreshMelts();
        Thread.Sleep(millisecondDelayForEventPublish * 100);

        mockery.VerifyAllExpectationsHaveBeenMet();

        Assert.AreEqual(vm.MeltsAvailable.Count, 2);
        Assert.AreEqual(vm.MeltsAvailable[0].MeltIdentifier, "TST0001");
        Assert.AreEqual(vm.MeltsAvailable[1].MeltIdentifier, "TST0002");

    }

测试在第一个 Assert.AreEqual 上始终失败。此时 vm.MeltsAvailable 为空。

如果我去掉所有线程并保持原样:

    public void RefreshMelts()
    {
        MeltsAvailable.Clear();
        IList<MeltDto> meltDtos = meltingAppService.GetActiveMelts();
        foreach (MeltDto availableMelt in meltDtos)
        {
            MeltsAvailable.Add(availableMelt);
        }
        OnPropertyChanged("MeltsAvailable");
    }

测试通过。

因此,很明显,它不喜欢线程的某些东西 - 但即使打开 Debug->Exceptions->CLR Exceptions->Thrown 并关闭 Just My Code,我在 RefreshMelts 中也没有任何异常。

最奇怪的是,我将 MeltDto 对象加载到 MeltsAvailable 集合中的 Dispatcher.Invoke 调用似乎从未被调用过。我可以用断点覆盖整个部分,它们永远不会被击中。在我的测试中将 Thread.Sleep 时间提高到甚至高达 10 秒也没有任何改变。

为什么?为什么该部分没有执行,为什么我不能进入或闯入它,为什么我没有收到异常,为什么它在执行中可以正常工作,但在测试中却不行?

非常感谢, 史蒂夫

【问题讨论】:

    标签: c# wpf testing dispatcher nmock


    【解决方案1】:

    Dispatcher 是一个与执行线程相关联的消息循环。当主线程空闲时,它会处理其队列中的项目。在单元测试中,这永远不会发生。线程忙,测试完成后退出。

    如果您使用 Visual Studio 运行测试,则可以打开代码覆盖率突出显示,您会看到 Dispatcher.Invoke() 中的代码从未被调用(它将显示为红色)。

    DispatcherFrame 可用于触发 Dispatcher 处理排队的消息。将以下帮助程序类添加到您的单元测试项目中:

    public static class DispatcherHelper 
    { 
        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; 
        } 
    }
    

    在测试结束时(在断言之前)调用 DispatcherHelper.DoEvents()。这将触发 Dispatcher 处理未完成的事件,例如将项目添加到视图模型的可观察集合的事件。然后,您可以检查视图模型的属性以验证它们是否设置正确。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-09
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 2021-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多