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);
}
ディスパッチャを使用しないでください。[ソース](https://github.com/reactiveui/ReactiveUI/blob/rxui6-master/ReactiveUI/RxApp.cs#L66)のあなたが単体テストであることを検出し、 'CurrentThreadScheduler'を使用するはずです。ユニットテストで 'ModeDetector.InUnitTestRunner()'は何を返しますか? –
False。問題はCommandManager.InvalidateRequerySuggested()を呼び出すことです。その結果、RaiseAndSetIfChangedが呼び出されると、その関数を呼び出すディスパッチャが作成され、ディスパッチャが見つかります。それをテストすることができます - ユニットテストを呼び出しますCommandManager.InvalidateRequerySuggested(); RaiseAndSetIfChangedを呼び出します。 OnNextを呼び出した後にScheduledSubjectの中にブレークポイントを置くと、それはtryToCreateScheduler()を呼び出してディスパッチャ – MistyK
を返します。それはfalseではありません。 – MistyK