2016-05-11 17 views
2

ReactiveUI 6.5.0への移行後、テストが失敗し始めました。 Resharper Test Runnerの動作は非常に奇妙です。テストを次々に実行しているとき - テストは合格しますが、ビッグバンのアプローチで実行すると - ユニットテストを実行します(約1kユニットテストがあります)。テストに失敗しています。リアクティブ6.5.0マイグレーション後のReactiveCommand

 [Test] 
     public void TestThat_CallingRun_CallsLogin() 
     { 
      //act 
      _sut.Actions.Single().Command.Execute(null); 

      //assert 
      _controller.AssertWasCalled(x => x.Login()); 
     } 

     public ViewModel(IWfController workflowController) 
     { 
      _workflowController = workflowController; 

      _runProcessCommand = ReactiveCommand.Create(CanRunProcess()); 
      _runProcessCommand.Subscribe(RunImpl); 
     } 

     private void RunImpl(object obj) 
     { 
      _workflowController.Login(); 
     } 

_sut.Actions.Single().Command戻り_runProcessCommand:これはその一つです。

このテストではReactiveUI 4.5を使用しました。 同期して実行することは保証されていますか?もしそうでなければ、今それをテストする正しい方法は何ですか?それらを次々と実行する間に異なる動作があるのはなぜですか?私はどんな考えにもうれしいだろう。

編集:

私は(複数のユニットテストを実行している場合のみ)RunImplが別のスレッドで呼び出されていることに気付きましたと私は

_runProcessCommand.Subscribe(RunImpl); 

を交換する場合、それは何も変更はありません

_runProcessCommand.ObserveOn(RxApp.MainThreadScheduler).Subscribe(RunImpl) 

RunImplはまだ別のスレッドで呼び出されているため、BEFOREコマンドが実行されたときにアサートされます。

編集2: これはNUnitで呼び出す修正です[SetUp]機能。私がScheduler.Immediateで観察するとうまくいきません。私は複数のテストを実行すると、デフォルトでイミディエイトに設定されていない理由は何ですか?

RxApp.MainThreadScheduler = Scheduler.Immediate; 
RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; 

編集(これは、すべての失敗のテストのために動作しません):WaitForDispatcherSchedulerにRX源に深くデバッグ後 を私が気づいたこと:

IScheduler attemptToCreateScheduler() 
     { 
      if (_innerScheduler != null) return _innerScheduler; 
      try { 
       _innerScheduler = _schedulerFactory(); 
       return _innerScheduler; 
      } catch (Exception) { 
       // NB: Dispatcher's not ready yet. Keep using CurrentThread 
       return CurrentThreadScheduler.Instance; 
      } 
     } 
RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(() => DispatcherScheduler.Current); 

単一のテストのためのスケジューラの工場を呼び出しますそれは例外をスローし、CurrentThreadScheduler.Instanceを返しています。 複数のテストを実行する場合Dispatcher.Currentはインスタンスを返します。誰が私にここで何が起こっているのか説明できますか?

シンプル再現可能テスト:

[Test] 
    public void Test1() 
    { 
     var disp =DispatcherScheduler.Current; // throws exception (System.InvalidOperationException : The current thread has no Dispatcher associated with it.) when   running single tests, not throwing exception when running multiple tests. 
    } 

編集2: DispatcherScheduler.Currentは異なる値を返すことが原因となっている私を発見しました。前のテストの1つがDelegateCommandを使用しています。CommandManager.InvalidateRequerySuggested();ディスパッチャーと値を作成するコールはattemptToCreateScheduler()によって返されます。これはイミディエイトスケジューラではなくディスパッチャーで呼び出されるため、残りのテストは失敗します。

私が仕事で経験したこの動作をし、私は自宅でそれを再現することができませんでした:

EDIT3)即時スケジューラのように振る舞います。しかし、Reactiveについて私が理解できないものがない限り、何かが間違っていなければならないと確信しています。私はプロジェクト全体を含める必要はないと考えています。この例では、その関数を使用するとスケジューラが現在のスレッドではなくディスパッチャであることを正確に示しています。それをデバッグして、WaitForDispatcherSchedulerクラスのattemptToCreateScheduler()関数でこれらの2つの呼び出しの間で異なる動作に気づくでしょう。 ReactiveUIはバージョン7.0です。

[Test] 
     public void TestMethod1() 
     { 
      RxApp.MainThreadScheduler.Schedule<string>(null, (x, y) => Disposable.Empty); 
      CommandManager.InvalidateRequerySuggested(); 
      RxApp.MainThreadScheduler.Schedule<string>(null, (x, y) => Disposable.Empty); 
     } 
+0

ディスパッチャを使用しないでください。[ソース](https://github.com/reactiveui/ReactiveUI/blob/rxui6-master/ReactiveUI/RxApp.cs#L66)のあなたが単体テストであることを検出し、 'CurrentThreadScheduler'を使用するはずです。ユニットテストで 'ModeDetector.InUnitTestRunner()'は何を返しますか? –

+0

False。問題はCommandManager.InvalidateRequerySuggested()を呼び出すことです。その結果、RaiseAndSetIfChangedが呼び出されると、その関数を呼び出すディスパッチャが作成され、ディスパッチャが見つかります。それをテストすることができます - ユニットテストを呼び出しますCommandManager.InvalidateRequerySuggested(); RaiseAndSetIfChangedを呼び出します。 OnNextを呼び出した後にScheduledSubjectの中にブレークポイントを置くと、それはtryToCreateScheduler()を呼び出してディスパッチャ – MistyK

+0

を返します。それはfalseではありません。 – MistyK

答えて

1

コマンドを実行する際の非同期性と同期テストの間に不一致があります。カバーの下

ExecuteだけExecuteAsync().Subscribe()を実行しますので、このすべてが行いますが、コマンドが実行されているを開始で、コマンドは、それが返された時間で実行が終了しているだろうという保証はありません。特定のテストでは、使用されているスケジューラに完全に依存しています。あなたが非同期的に任意の副作用をアサートする前にフィニッシュにコマンドを待ってやるべきことは何

[Test] 
public async Task Test() 
{ 
    // arrange 

    await command.ExecuteAsync(); 

    // assert 
} 

余談として、様々なコメントで述べたように、私は意図があるとは思いませんMainThreadSchedulerがユニットテストシナリオでディスパッチャを使用するようにします。これはバグかもしれません。詳細については、this github issueを参照してください。

+0

edit3 – MistyK

+0

@Zbigniewを見てください。スプラットのモード検出器をデバッグして、単体テストランナーで動作しているとは思わない理由を理解する必要があります。コマンドがディスパッチャを作成するという事実はそれほど重要ではありません。 - MainThreadSchedulerは、最初にWaitForDispatcherSchedulerに設定されていてはなりません。 –

+0

本当ですか?これはRegistrations.csの行です #if!MONO &&!NETFX_CORE RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(>)=> DispatcherScheduler.Current); 私はスプレイについてなぜ言っているのか分かりません。 Mode.IsUnitTestRunner()は、毎回trueを返します。 – MistyK

関連する問題