迁移到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();
}
_sut.Actions.Single().Command
返回_runProcessCommand
。
此测试通过了ReactiveUI 4.5。 是否保证同步执行?如果不是 - 现在测试它的正确方法是什么?为什么一个接一个地运行它们之间会有不同的行为?我会很高兴任何想法。
编辑:
我注意到RunImpl是在另一个线程上调用的(仅在运行多个单元测试时),如果我替换它就不会改变任何东西
_runProcessCommand.Subscribe(RunImpl);
与
_runProcessCommand.ObserveOn(RxApp.MainThreadScheduler).Subscribe(RunImpl)
仍在另一个线程上调用RunImpl,因此在执行BEFORE命令时会执行assert。
编辑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();
创建调度程序,值由attemptToCreateScheduler()
返回,导致其余测试失败,因为它们未在Immediate Scheduler上调用,而是Dispatcher(它是什么?在单元测试中?它的行为不像立即调度程序)
EDIT3:
我在工作中经历过的这种行为,我无法在家中重现它。但我确信,除非有一些我对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);
}
答案 0 :(得分:1)
执行命令的异步性质与同步测试之间存在不匹配。
在封面Execute
下只运行ExecuteAsync().Subscribe()
,所以这一切都是 start 命令运行,无法保证命令将在时间内完成执行它返回。它在某些测试中所做的事实完全取决于使用的调度程序。
你应该做的是在断言任何副作用之前异步等待命令完成:
[Test]
public async Task Test()
{
// arrange
await command.ExecuteAsync();
// assert
}
顺便说一句,正如各种评论所述,我认为MainThreadScheduler
的目的不是在单元测试场景中使用调度程序。看来这可能是一个错误。有关详细信息,请参阅this github issue。