加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

多线程 – 在单元可测试的MVVM代码中使用Dispatcher

发布时间:2020-12-15 02:23:24 所属栏目:Java 来源:网络整理
导读:我有一个MVVM-lite应用程序,我想要单元可测试.该模型使用System.Timers.Timer,因此更新事件最终在后台工作线程上.这个单元测试很好,但是在运行时抛出了System.NotSupportedException“这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其Sour
我有一个MVVM-lite应用程序,我想要单元可测试.该模型使用System.Timers.Timer,因此更新事件最终在后台工作线程上.这个单元测试很好,但是在运行时抛出了System.NotSupportedException“这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection.”我曾希望MVVM-lite类Threading.DispatcherHelper可以解决问题,但调用DispatcherHelper.CheckBeginInvokeOnUI导致我的单元测试失败.这是我在视图模型中最终得到的代码

private void locationChangedHandler(object src,LocationChangedEventArgs e)
{
    if (e.LocationName != this.CurrentPlaceName)
    {
        this.CurrentPlaceName = e.LocationName;
        List<FileInfo> filesTaggedForHere = Tagger.FilesWithTag(this.CurrentPlaceName);

        //This nextline fixes the threading error,but breaks it for unit tests
        //GalaSoft.MvvmLight.Threading.DispatcherHelper.CheckBeginInvokeOnUI(delegate { updateFilesIntendedForHere(filesTaggedForHere); });

        if (Application.Current != null)
        {
            this.dispatcher.Invoke(new Action(delegate { updateFilesIntendedForHere(filesTaggedForHere); }));
        }
        else
        {
            updateFilesIntendedForHere(filesTaggedForHere);
        }
    }
}
private void updateFilesIntendedForHere(List<FileInfo> filesTaggedForHereIn)
{
    this.FilesIntendedForHere.Clear();
    foreach (FileInfo file in filesTaggedForHereIn)
    {
        if (!this.FilesIntendedForHere.Contains(file))
        {
            this.FilesIntendedForHere.Add(file);
        }
    }
}

我在http://kentb.blogspot.com/2009/04/mvvm-infrastructure-viewmodel.html中尝试过这个技巧,但在单元测试期间对Dispatcher.CurrentDispatcher上的Invoke调用无法运行,因此失败了.这就是为什么我在运行测试而不是应用程序时直接调用辅助方法的原因.

这可能不对 – ViewModel不应该关心它的调用位置.任何人都可以看到为什么Kent Boogaart的调度程序方法和MVVM-lite DispatcherHelper.CheckBeginInvokeOnUI都不能在我的单元测试中工作?

解决方法

我是这样做的:

class MyViewModel() {
    private readonly SynchronizationContext _syncContext;

    public MyViewModel() {
        _syncContext = SynchronizationContext.Current; // or use DI
    )

    ...

    public void SomeTimerEvent() {
        _syncContext.Post(_ => UpdateUi(),null);
    }
}

默认上下文将是测试中的线程池和UI中的调度程序.如果您想要其他一些行为,也可以轻松创建自己的测试上下文.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读