针对WPF和分派器的NMock问题测试 [英] NMock issue testing against WPF and Dispatcher

查看:74
本文介绍了针对WPF和分派器的NMock问题测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Here's one for the threading junkies out there. I've got this method:

    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的Observable集合。

This is defined in a WPF user control. MeltsAvailable is an ObservableCollection of MeltDtos. This code works beautifully when running in the application itself.

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

The trouble is that I would like to create a unit test, using NMock, to verify the results of this method - specifically, that once it's called, the MeltsAvailable property has some items. Here's the test method:

    [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此时为空。

The test consistently fails on the first Assert.AreEqual. vm.MeltsAvailable is empty at that point.

如果我删除所有线程并将其保留为:

If I strip out all the threading and leave it just as:

    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中也没有任何异常。

So, obviously, there's something it doesn't like about the threads - but even turning on Debug->Exceptions->CLR Exceptions->Thrown, and turning off Just My Code, I get no exceptions at all in RefreshMelts.

最奇怪的部分是Dispatcher.Invoke调用,从未将MeltDto对象加载到MeltsAvailable集合中。我可以用断点覆盖整个部分,但它们永远不会受到打击。将我测试中的Thread.Sleep时间延长到甚至十秒都不会改变。

The strangest part is that the Dispatcher.Invoke call where I load the MeltDto objects into the MeltsAvailable collection never seems to be called. I can blanket the whole section with breakpoints, and they never get hit. Boosting the Thread.Sleep time in my test to even as high as ten seconds changes nothing.

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

Why? Why is that section not executing, why can't I step into it or break into it, why am I not getting exceptions, why does it work fine in execution but not in a test?

非常感谢,
Steve

Thanks much, Steve

推荐答案

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

The Dispatcher is a message loop that is tied to the executing thread. It processes the items in its queue when the main thread is idle. In a unit test, that never happens. The thread is busy and then it exits when the test is completed.

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

If you're using Visual Studio to run your tests, you can turn on code coverage highlighting and you'll see that code inside Dispatcher.Invoke() is never called (it will be displayed in red).

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

A DispatcherFrame can be used to trigger the Dispatcher to process queued messages. Add the following helper class to your unit test project:

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处理未完成的事件,例如将事件添加到视图模型的可观察集合的事件。然后,您可以检查视图模型的属性以验证其设置正确。

At the end of your test (prior to the assertions) call DispatcherHelper.DoEvents(). This will trigger the Dispatcher to process outstanding events, such as the ones that add items to the view model's observable collection. You can then inspect the view model's properties to verify that they were set correctly.

这篇关于针对WPF和分派器的NMock问题测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆