2011-01-24 59 views
4

我有一个MVVM-lite应用程序,我想单元测试。该模型使用System.Timers.Timer,因此更新事件在后台工作线程上结束。这个单元测试很好,但在运行时抛出了System.NotSupportedException“这种类型的CollectionView不支持从与分派器线程不同的线程对其SourceCollection的更改。”我曾希望MVVM-lite类的Threading.DispatcherHelper能够解决问题,但在我的单元测试失败时调用DispatcherHelper.CheckBeginInvokeOnUI结果。下面是我结束了在视图模型在单元可测试的MVVM代码中使用分派器

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调用的代码失败的单元测试过程中运行,所以它失败了。这就是为什么我直接调用助手方法,如果运行在测试中而不是应用程序。

这不可能是正确的 - ViewModel不应该关心它从哪里被调用。任何人都可以看到为什么肯特Boogaart的调度方法,也没有MVVM-lite DispatcherHelper.CheckBeginInvokeOnUI在我的单元测试中工作?

回答

1

我不喜欢这样写道:

class MyViewModel() { 
    private readonly SynchronizationContext _syncContext; 

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

    ... 

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

默认情况下会在你的UI测试和调度线程池。如果您需要其他一些行为,您也可以轻松创建您自己的测试环境。

0

我不相信MVVM-lite中有这个简单的答案。您有调用DispatcherHelper.CheckBeginInvokeOnUI的正确解决方案。但是,单元测试运行时,UI不存在,并且DispatcherHelper将无法正常运行。我使用ReactiveUI。它的版本DispatcherHelper.CheckBeginInvokeOnUI(RxApp.DeferredScheduler)将检查以确定它是否在单元测试中运行。如果是,它将在当前线程上运行,而不是尝试编组到不存在的UI线程。您可以使用此代码将自己的支票构建到DispatcherHelper中。相关代码位于RxApp.cs方法InUnitTestRunner()(行196)中。这是相当hacky,但它的工作原理,我不认为有更好的办法。

0

我只是在我的ViewModelUnitTestBase中调用Initialize方法,它工作正常。 确保DispatcherHelper.UIDispatcher不为空。

public abstract class ViewModelUnitTestBase<T> where T : ViewModelBase 
{ 
    private T _viewModel = default(T); 
    public T ViewModel 
    { 
     get { return _viewModel; } 
     set { _viewModel = value; } 
    } 

    static ViewModelUnitTestBase() 
    { 
     DispatcherHelper.Initialize(); 
    } 
} 
相关问题