MVVM Light DispatcherHelper&单元测试w / MSTest

时间:2011-01-18 19:43:27

标签: wpf unit-testing mvvm-light

我考虑将此作为另一个问题放在这个帖子中:

Unit testing with MVVM Light & DispatcherHelper

但是当我深入研究它时,我认为它有点不同。

我有一个包含视图模型/视图的项目,一个包含模型的单独项目,以及包含模型测试的第三个单独的单元测试项目(MSTest)。我正在使用MVVM Light的DispatcherHelper来帮助我在后台任务中工作,但是要将错误从该任务返回到UI。

视图模型/视图项目具有App.xaml.cs文件,该文件包含OnStartup事件处理程序,我在其中放置了DispatcherHelper.Initialize()调用。其中一个VM使用delegate.BeginInvoke()在另一个线程上调用一个模型的长期运行方法:

     FileProcessDelegate processor = GenerateAddressingFile;
     processor.BeginInvoke(FileGenerationComplete, null);

在这个长时间运行的方法中,它调用实用程序方法将错误发送回UI。这些是使用DispatcherHelper.CheckBeginInvokeOnUI()

发送的mvvm-light消息的形式
    internal static void ReportErrorToUi(IFileError error)
    {
        DispatcherHelper.CheckBeginInvokeOnUI(
            () => Messenger.Default.Send(new GenericMessage<IFileError>(error), MessageTokens.ReportFileError));
    }

这一切似乎在应用程序中运行良好。

在模型的其中一个单元测试类中,我尝试检查长时间运行的方法是否在发生错误时正确发送消息。在该测试类中,我在MSTest类初始化方法中调用了DispatcherHelper.Initialize()。

    #region Additional test attributes
    // 
    //You can use the following additional attributes as you write your tests:
    //
    //Use ClassInitialize to run code before running the first test in the class
    [ClassInitialize]
    public static void MyClassInitialize(TestContext testContext)
    {
        DispatcherHelper.Initialize();
    }

测试注册应该发送的消息,然后直接调用模型的方法,而不在单独的线程上调用它。据我了解,因为发送消息的调用由模型中的DispatcherHelper.CheckBeginInvokeOnUI()包装,无论是在后台线程上还是在UI线程上发生,都应该做正确的事情。

在这个检查方法的类的第一个测试中,一切正常。该测试接收发回的错误消息,并以漂亮的颜色传递。

当同一类中的后续测试运行时,测试永远不会收到消息。当我在后续测试中逐步完成它时,我注意到当我到达DispatcherHelper.CheckBeginInvokeOnUI()方法时,DispatcherHelper.UIDispatcher.Thread实例表明它已经死了。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

我不确定这个解决方案是否可以包含在Mvvm Light中,或者它是否违反了框架的一些基本前提,但是我这样做是为了让它工作:

我创建了DispatcherHelper类的自定义本地版本,它与Mvvm Light中的内容略有不同,即Initialize()方法中的逻辑:

    public static void Initialize()
    {
        if (UIDispatcher != null &&
            UIDispatcher.Thread.IsAlive)
        {
            return;
        }
        UIDispatcher = Dispatcher.CurrentDispatcher;
    }

此处,更改是添加:

&& UIDispatcher.Thread.IsAlive

条件。这允许我在单元测试类的MyTestInitialize()方法中调用DispatcherHelper.Initialize()。这样,如果与UIDispatcher关联的先前线程已经死亡,则DispatcherHelper将获取将在其上运行此当前测试方法的新Dispatcher / Thread。

显然,在VSTS测试框架中,没有一个“中心”线程就像应用程序中的UI线程一样。 MyClassInitialize()方法在一个线程上执行,然后测试在一个完全不同的线程上运行。 Mvvm Light定义不允许我通过再次调用DispatcherHelper.Initialize()来将DispatcherHelper挂钩到新的测试线程,因为UIDispatcher不是null,它的线程刚刚死了。

答案 1 :(得分:0)

测试异步代码很棘手。通常情况下,如果它工作一次,然后不再工作,听起来好像你没有正确地同步你的线程。在你的测试方法(主线程)中,你在等待后台线程执行吗?

Jonas Follesoe在Silverlight中写了一些关于异步单元测试的好文章,例如http://jonas.follesoe.no/2007/09/18/unit-testing-event-based-asynchronous-code/。这通常是我对异步代码进行单元测试的方式。

希望这有帮助, 劳伦