【发布时间】:2018-02-04 13:26:53
【问题描述】:
归结:我需要在我的单元测试线程中延迟执行,以便可观察对象有时间更新属性。有没有一种响应式方法可以做到这一点而不必求助于 Thread.Sleep?
我有一个管理“事物”的 ReactiveList 的 ViewModel。 ThingViewModel 和主 ViewModel 都是从 ReactiveObject 派生的。 Thing 有一个 IsSelected 属性,而 ViewModel 有一个 SelectedThing 属性,我根据观察 IsSelected 上的变化保持同步。我有几个使用 ViewModel 的不同视图,这使我可以很好地在这些视图之间同步选定的事物。它有效。当我尝试对这种交互进行单元测试时,问题就出现了。
ViewModel 在它的构造函数中有这个订阅:
Things.ItemChanged.Where(c => c.PropertyName.Equals(nameof(ThingViewModel.IsSelected))).ObserveOn(ThingScheduler).Subscribe(c =>
{
SelectedThing = Things.FirstOrDefault(thing => thing.IsSelected);
});
在我的单元测试中,这个断言总是失败:
thingVM.IsSelected = true;
Assert.AreEqual(vm.SelectedThing, thingVM);
但是这个断言总是通过:
thingVM.IsSelected = true;
Thread.Sleep(5000);
Assert.AreEqual(vm.SelectedThing, thingVM);
基本上,我需要等待足够长的时间让订阅完成更改。 (我真的不需要那么长时间等待,因为在 UI 中运行时,它非常快。)
我尝试在我的单元测试中添加一个观察者来等待该处理,希望它们是相似的。但那是洗过的。这是没有用的。
var itemchanged = vm.Things.ItemChanged.Where(x => x.PropertyName.Equals("IsSelected")).AsObservable();
. . .
thingVM.IsSelected = true;
var changed = itemChanged.Next();
Assert.AreEqual(vm.SelectedThing, thingVM);
移动 itemChanged.Next 没有帮助,也没有通过在 changed 上调用 .First() 或 .Any() 来触发迭代(这两个都挂起进程,因为它阻塞了线程以获得永远不会的通知发生)。
所以。是否有一种反应方式来等待该交互,以便我可以断言属性更改正在正确发生?我用 TestScheduler 搞砸了一些(无论如何我都需要为 UI 手动设置调度程序,所以这并不难),但这似乎并不适用,因为给予调度程序的实际操作发生在 ItemChanged 上的 ViewModel 中,我找不到以 TestScheduler 似乎设置为工作的方式触发该触发器的方法。
【问题讨论】:
-
如果目标是 INPC 派生的,则插入/订阅属性更改事件并设置可以断言的标志。
-
ReactiveObject 支持 INPC,但由于我在 Reactive Land 中玩游戏,因此事件处理程序中的断言不太理想。如果我想走那条路,我可以订阅改变 observable 的属性并在那里断言。我更愿意像上面那样断言实际属性,但我可以将其用作后备...
-
这可能就足够了。但是,Thread.Sleep 解决方案也是如此。问题的原因是我想知道是否有一种方法可以做到这一点,即“反应式原生”。如果有的话,那么我会学到一些关于 Reactive 的新东西和有用的东西,这些东西将适用于这一刻。
-
我可能应该将集合设为私有,实际上,因为除了 ViewModel 设置属性之外我什么都不想要。不过,让它受到保护以便我可以创建覆盖/模拟比我想要的更痛苦。
标签: c# unit-testing reactiveui