MVVM Light DispatcherHelper &使用 MSTest 进行单元测试

发布于 2024-10-12 19:18:53 字数 1887 浏览 0 评论 0原文

我考虑将其作为此线程中的一个附加问题:

使用 MVVM Light & 进行单元测试DispatcherHelper

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

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

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

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

在此长时间运行的方法中,它调用实用程序方法将错误发送回 UI。这些消息采用 mvvm-light 消息的形式,使用 DispatcherHelper.CheckBeginInvokeOnUI() 发送,

    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 实例说它已死。

有什么想法吗?

I considered putting this as an additional question in this thread:

Unit testing with MVVM Light & DispatcherHelper

but as I delved into it, I think it's a little different.

I have one project containing view models/views, a separate project containing models, and a third separate unit test project (MSTest) containing tests for the models. I'm using MVVM Light's DispatcherHelper to help me do work in a background task, but to get errors from that task back to the UI.

The view model/view project has the App.xaml.cs file, which contains the OnStartup event handler, in which I've put the DispatcherHelper.Initialize() call. One of the VMs invokes a call to one of the model's long-running methods on another thread using delegate.BeginInvoke():

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

In this long-running method, it calls a utility method to send errors back to the UI. These are in the form of mvvm-light messages that are sent using the DispatcherHelper.CheckBeginInvokeOnUI()

    internal static void ReportErrorToUi(IFileError error)
    {
        DispatcherHelper.CheckBeginInvokeOnUI(
            () => Messenger.Default.Send(new GenericMessage<IFileError>(error), MessageTokens.ReportFileError));
    }

This all seems to work fine in the app.

In one of the unit test classes for the model, I attempt to check that the long-running method correctly sends messages when errors occur. In that test class, I have a call to DispatcherHelper.Initialize() in the MSTest class initialization method.

    #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();
    }

The test registers for the message that should be sent, then calls the model's method directly, without invoking it on a separate thread. As I understand it, because the call to send the message is wrapped by DispatcherHelper.CheckBeginInvokeOnUI() in the model, it should do the right thing regardless of whether it is occurring on a background thread or on the UI thread.

In the first test of this class that checks the method, everything works fine. The test receives the error messages being sent back and passes with flying colors.

When subsequent tests in the same class run, the messages are never received by the test. When I've stepped through it on these subsequent tests, I notice that when I get to the DispatcherHelper.CheckBeginInvokeOnUI() method, the DispatcherHelper.UIDispatcher.Thread instance says it's dead.

Any thoughts?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

缘字诀 2024-10-19 19:18:54

测试异步代码很棘手。通常,如果它工作一次,然后不再工作,那么听起来您没有正确同步线程。在您的测试方法(主线程)中,您是否正在等待后台线程执行?

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

希望这有帮助,
洛朗

Testing async code is tricky. Typically, if it works once, and then not anymore, it sounds like you are not synchronizing your threads correctly. In your test method (main thread), are you waiting for the background thread to execute?

Jonas Follesoe wrote a few good articles about async unit testing in Silverlight, for example http://jonas.follesoe.no/2007/09/18/unit-testing-event-based-asynchronous-code/. This is typically how I unit test my async code too.

Hope this helps,
Laurent

橘虞初梦 2024-10-19 19:18:53

我不确定这个解决方案是否可以包含在 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,它的线程只是死了。

I'm not sure if this solution is something that can be included in Mvvm Light, or if it violates some base premises of the framework, but here's what I did to get this to work:

I made a custom local version of the DispatcherHelper class that has one minor difference from what's in Mvvm Light, namely the logic in the Initialize() method:

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

Here, the change is the addition of:

&& UIDispatcher.Thread.IsAlive

to the condition. This allows me to call DispatcherHelper.Initialize() in the MyTestInitialize() method of my unit test class. That way, if the previous thread that the UIDispatcher was associated with has died, the DispatcherHelper gets the new Dispatcher/Thread that this current test method will be run on.

Apparently, in the VSTS Testing framework, there is not a "central" thread that acts like the UI thread in an application. The MyClassInitialize() method was executed on one thread, and then the tests were run on a completely different thread. The Mvvm Light definition wouldn't let me hook the DispatcherHelper up to the new test thread by calling DispatcherHelper.Initialize() again because the UIDispatcher wasn't null, it's thread was just dead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文