2011-01-24 3 views
4

ユニットテスト可能にしたいMVVM-Liteアプリケーションがあります。モデルはSystem.Timers.Timerを使用するため、updateイベントはバックグラウンドのワーカースレッドで終了します。このユニットテストは正常ですが、実行時にSystem.NotSupportedExceptionがスローされました。「このタイプのCollectionViewは、Dispatcherスレッドとは異なるスレッドからのSourceCollectionへの変更をサポートしていません。 MVVM-liteクラスのThreading.DispatcherHelperが問題を修正することを期待していましたが、DispatcherHelper.CheckBeginInvokeOnUIを呼び出すとユニットテストが失敗しました。ここで私が終わったコードは、ビューモデルにユニットテスト可能なMVVMコードでDispatcherを使用

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はどこから呼び出されても構いません。誰も私の単体テストでKent 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)にあります。それはかなりハッキーですが、それは動作し、私は良い方法があるとは思わない。

0

私はViewModelUnitTestBaseでInitializeメソッドを呼び出しても問題ありません。 DispatcherHelper.UIDispatcherがnullでないことを確認してください。

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(); 
    } 
} 
関連する問題