Reactive 6.5.0迁移后的ReactiveCommand



迁移到ReactiveUI 6.5.0后,我的测试开始失败。Resharper Test Runner的行为非常奇怪。当我一个接一个地运行测试时——测试通过了,但如果我用大爆炸的方法运行它们——运行单元测试(我们有大约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();
        }

1返回CCD_ 2。

此测试通过了ReactiveUI 4.5。是否保证同步执行?如果没有,现在测试它的正确方法是什么?为什么在一个接一个地运行它们之间会有不同的行为?我很乐意有任何想法。

编辑:

我注意到RunImpl是在另一个线程上调用的(只有在运行多个单元测试时),如果我替换,它不会改变任何东西

_runProcessCommand.Subscribe(RunImpl);

带有

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

RunImpl仍在另一个线程上调用,因此在执行命令之前进行断言。

编辑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以返回不同的值。之前的一个测试是使用DelegateCommand,它在内部调用CommandManager.InvalidateRequerySuggested();,CCD_8创建调度器,值由attemptToCreateScheduler()返回,导致其余测试失败,因为它们不是在即时调度器上调用的,而是在调度器上调用(单元测试中是什么?它的行为不像即时调度器)

第3版:

我在工作中经历过这种行为,但在家里我无法重现。但我确信肯定是出了什么问题,除非我对Reactive有什么不理解的地方。我认为包含整个项目是没有意义的,这个例子确切地表明,如果使用该函数,调度器是调度器,而不是当前线程。请调试它,您会注意到这两个调用在WaitForDispatcherScheduler类的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);
        }

执行命令的异步性质与同步测试之间不匹配。

在幕后,_sut.Actions.Single().Command0只是运行ExecuteAsync().Subscribe(),所以所有这些都将启动正在运行的命令,无法保证该命令在返回时已完成执行。它在某些测试中的作用完全取决于所使用的调度器。

您应该做的是异步等待命令完成,然后再断言任何副作用:

[Test]
public async Task Test()
{
    // arrange
    await command.ExecuteAsync();
    // assert
}

顺便说一句,正如在各种评论中所指出的,我不认为MainThreadScheduler打算在单元测试场景中使用调度器。看起来这可能是一个错误。有关更多详细信息,请参阅本期github。

相关内容

  • 没有找到相关文章

最新更新