迁移后的 Reactive 6.5.0 ReactiveCommand
Reactive 6.5.0 ReactiveCommand after migration
迁移到 ReactiveUI 6.5.0 后,我的测试开始失败。 Resharper Test 运行ner 的行为非常奇怪。当我 运行 一个接一个地进行测试时 - 测试通过了,但如果我 运行 他们采用大爆炸方法 - 运行 单元测试(我们有大约 1k 单元测试)一些测试失败。这是其中之一:
[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
returns _runProcessCommand
.
此测试已通过 ReactiveUI 4.5。
是否保证同步执行?如果不是 - 现在测试它的正确方法是什么?为什么 运行 一个接一个地表现不同?我很乐意提出任何想法。
编辑:
我注意到 运行Impl 正在不同的线程上调用(仅当 运行 进行多个单元测试时)并且如果我替换 [=26= 它不会改变任何东西]
_runProcessCommand.Subscribe(RunImpl);
和
_runProcessCommand.ObserveOn(RxApp.MainThreadScheduler).Subscribe(RunImpl)
运行Impl 仍在另一个线程上调用,因此在执行命令之前进行断言。
编辑 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);
当为单个测试调用调度程序工厂时,它抛出异常并 returning CurrentThreadScheduler.Instance
。
当 运行 多次测试 Dispatcher.Current
return 一个实例。谁能给我解释一下这是怎么回事?
简单的可重现测试:
[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 到 return 不同值的原因。之前的测试之一是使用 DelegateCommand,它在内部调用 CommandManager.InvalidateRequerySuggested();
创建调度程序,值由 attemptToCreateScheduler()
编辑 return 导致其余测试失败,因为它们没有在 Immediate Scheduler 上调用但是调度程序(它在单元测试中是什么?它的行为不像立即调度程序)
编辑 3:
这种行为我在工作中遇到过,但我无法在家里重现。但我确信一定有问题,除非我对 Reactive 有什么不了解的地方。我认为没有必要包括整个项目,这个例子准确地表明调度程序是调度程序而不是当前线程,如果你使用那个函数。请调试它,您会注意到在 WaitForDispatcherScheduler class 中的 attemptToCreateScheduler() 函数中这两个调用之间存在不同的行为。 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);
}
执行命令的异步性质与同步测试不匹配。
在幕后 Execute
只是运行 ExecuteAsync().Subscribe()
,所以这将做的只是 start 命令 运行,不能保证该命令将在 returns 时完成执行。它在某些测试中所做的事实完全取决于使用的调度程序。
您应该做的是在断言任何副作用之前异步等待命令完成:
[Test]
public async Task Test()
{
// arrange
await command.ExecuteAsync();
// assert
}
顺便说一句,如各种评论中所述,我认为 MainThreadScheduler
的目的不是在单元测试场景中使用调度程序。看起来这可能是一个错误。有关详细信息,请参阅 this github issue。
迁移到 ReactiveUI 6.5.0 后,我的测试开始失败。 Resharper Test 运行ner 的行为非常奇怪。当我 运行 一个接一个地进行测试时 - 测试通过了,但如果我 运行 他们采用大爆炸方法 - 运行 单元测试(我们有大约 1k 单元测试)一些测试失败。这是其中之一:
[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
returns _runProcessCommand
.
此测试已通过 ReactiveUI 4.5。 是否保证同步执行?如果不是 - 现在测试它的正确方法是什么?为什么 运行 一个接一个地表现不同?我很乐意提出任何想法。
编辑:
我注意到 运行Impl 正在不同的线程上调用(仅当 运行 进行多个单元测试时)并且如果我替换 [=26= 它不会改变任何东西]
_runProcessCommand.Subscribe(RunImpl);
和
_runProcessCommand.ObserveOn(RxApp.MainThreadScheduler).Subscribe(RunImpl)
运行Impl 仍在另一个线程上调用,因此在执行命令之前进行断言。
编辑 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);
当为单个测试调用调度程序工厂时,它抛出异常并 returning CurrentThreadScheduler.Instance
。
当 运行 多次测试 Dispatcher.Current
return 一个实例。谁能给我解释一下这是怎么回事?
简单的可重现测试:
[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 到 return 不同值的原因。之前的测试之一是使用 DelegateCommand,它在内部调用 CommandManager.InvalidateRequerySuggested();
创建调度程序,值由 attemptToCreateScheduler()
编辑 return 导致其余测试失败,因为它们没有在 Immediate Scheduler 上调用但是调度程序(它在单元测试中是什么?它的行为不像立即调度程序)
编辑 3:
这种行为我在工作中遇到过,但我无法在家里重现。但我确信一定有问题,除非我对 Reactive 有什么不了解的地方。我认为没有必要包括整个项目,这个例子准确地表明调度程序是调度程序而不是当前线程,如果你使用那个函数。请调试它,您会注意到在 WaitForDispatcherScheduler class 中的 attemptToCreateScheduler() 函数中这两个调用之间存在不同的行为。 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);
}
执行命令的异步性质与同步测试不匹配。
在幕后 Execute
只是运行 ExecuteAsync().Subscribe()
,所以这将做的只是 start 命令 运行,不能保证该命令将在 returns 时完成执行。它在某些测试中所做的事实完全取决于使用的调度程序。
您应该做的是在断言任何副作用之前异步等待命令完成:
[Test]
public async Task Test()
{
// arrange
await command.ExecuteAsync();
// assert
}
顺便说一句,如各种评论中所述,我认为 MainThreadScheduler
的目的不是在单元测试场景中使用调度程序。看起来这可能是一个错误。有关详细信息,请参阅 this github issue。