Nach der Migration zu ReactiveUI 6.5.0 begannen meine Tests fehlzuschlagen. Das Verhalten von Resharper Test Runner ist sehr seltsam. Wenn ich Tests nacheinander abspiele - die Tests bestehen, aber wenn ich sie mit Big Bang Ansatz laufe - Run Unit Tests (wir haben etwa 1k Unit Tests) einige Tests scheitern. Dies ist einer von ihnen:Reactive 6.5.0 ReactiveCommand nach der Migration
[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
kehrt _runProcessCommand
.
Dieser Test wurde mit ReactiveUI 4.5 bestanden. Wird garantiert synchron ausgeführt? Wenn nicht - was ist der richtige Weg, es jetzt zu testen? Warum gibt es ein unterschiedliches Verhalten zwischen dem Ausführen von ihnen nacheinander? Ich würde mich für irgendwelche Ideen freuen.
Edit:
Ich habe bemerkt, dass RunImpl auf einem anderen Thread aufgerufen wird (nur wenn mehrere Komponententests ausgeführt wird) und es ändert nichts, wenn ich
ersetzen_runProcessCommand.Subscribe(RunImpl);
mit
_runProcessCommand.ObserveOn(RxApp.MainThreadScheduler).Subscribe(RunImpl)
RunImpl wird immer noch auf einem anderen Thread aufgerufen, daher wird BEFORE ausgeführt, bevor der Befehl ausgeführt wird.
Bearbeiten 2: Dies ist die Korrektur, die ich in NUnit [SetUp]
Funktion aufrufen. Wenn ich auf Scheduler.Immediate
beobachte, wird es nicht so gut funktionieren. Irgendwelche Ideen, warum es nicht standardmäßig auf Immediate gesetzt ist, wenn ich mehrere Tests durchführe? (Es funktioniert nicht für all vorgeschriebenen Prüfungen nicht)
RxApp.MainThreadScheduler = Scheduler.Immediate;
RxApp.TaskpoolScheduler = TaskPoolScheduler.Default;
Edit: Nach tiefen Debuggen in RX Quellen in WaitForDispatcherScheduler
Ich habe bemerkt, dass:
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);
Wenn Scheduler Fabrik für einzelnen Test Aufruf Es wirft eine Ausnahme auf und gibt CurrentThreadScheduler.Instance
zurück. Wenn mehrere Tests ausgeführt werden, gibt Dispatcher.Current
eine Instanz zurück. Kann mir jemand erklären, was hier abgeht?
Einfacher reproduzierbarer Test:
[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.
}
Edit 2: Ich habe gefunden, was DispatcherScheduler.Current unterschiedliche Werte zurückgeben verursacht. Einer der vorherigen Test verwendet DelegateCommand, die innerhalb CommandManager.InvalidateRequerySuggested();
CommandManager.InvalidateRequerySuggested();
ruft Dispatcher und Wert wird von attemptToCreateScheduler()
verursacht verursacht den Rest der Tests fehlgeschlagen, weil sie nicht auf Immediate Scheduler, sondern Dispatcher aufgerufen werden (was ist es in Unit-Tests? Es tut nicht verhalten sich wie sofort Scheduler)
Edit3:
Dieses Verhalten, das ich bei der Arbeit erlebt habe und ich konnte es nicht zu Hause reproduzieren. Aber ich bin mir sicher, dass etwas nicht in Ordnung ist, es sei denn, es gibt etwas, was ich nicht über Reactive verstehe. Ich denke, dass es keinen Sinn macht, das gesamte Projekt einzubeziehen. Dieses Beispiel zeigt genau, dass der Scheduler Dispatcher ist und nicht der aktuelle Thread, wenn Sie diese Funktion verwenden. Bitte debuggen Sie es und Sie werden ein anderes Verhalten zwischen diesen beiden Aufrufen in der Funktion "versuchenToCreateScheduler()" in der WaitForDispatcherScheduler-Klasse bemerken. ReactiveUI ist Version 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);
}
Es sollte nicht den Dispatcher überhaupt verwenden - Sie können [in der Quelle] (https://github.com/reaceuci/ReactiveUI/blob/rxui6-master/ReactiveUI/RxApp.cs#L66) sehen, dass Es sollte erkennen, dass Sie sich in einem Komponententest befinden und 'CurrentThreadScheduler' verwenden. Was gibt 'ModeDetector.InUnitTestRunner()' in Ihren Unit-Tests zurück? –
Falsch. Das Problem ruft CommandManager.InvalidateRequerySuggested() auf; Das erzeugt einen Dispatcher, der als Ergebnis beim Aufrufen von RaiseAndSetIfChanged diese Funktion aufruft und den Dispatcher findet. Sie können das testen - machen Sie einen Komponententest ein invoke CommandManager.InvalidateRequerySuggested(); und dann RaiseAndSetIfChanged aufrufen. Wenn Sie nach dem Aufruf von OnNext innerhalb von ScheduledSubject einen Haltepunkt setzen, wird er versuchToCreateScheduler() aufrufen und den Dispatcher zurückgeben. – MistyK
@up es ist nicht falsch. – MistyK