在我们的很多视图模型中,我们的代码看起来像这样:
void Process()
{
// do some work
...
// Dispatch some UI notification (purely visual related)
Application.Current.Dispatcher.BeginInvoke(() => ...);
}
我们使用Application.Dispatcher
来确保它在UI线程上运行,因为Dispatcher.Current
可能不是UI调度器。
调度调用的原因是无关紧要的,不会影响控制流或逻辑,但是,当我们测试视图模型时,我们显然遇到了Application.Current
为null的问题。
假设被调度的代码对测试没有影响。你认为什么是最好的方法来解决它是空的问题?
我能想到几个
1.简单地使用Application.Current?.Dispatcher()
。这是代码的味道吗
1.拥有某种可模拟的IApplicationDispatcher
,每个ViewModel都可以访问。这为我们提供了最强大的功能,但同时,现在为每个视图模型提供一个额外的构造函数参数是令人讨厌的。
1.确保测试在初始化时与创建的新应用程序一起运行。这是丑陋的,并且需要是所有UI测试共享的静态应用程序。
2条答案
按热度按时间5f0d552i1#
Dispatcher
在视图模型中通常是不需要的。这是因为视图模型一般不应该处理UI元素。换句话说,扩展DispatcherObject
的类型不应该在视图之外引用。当视图模型类实现
INotifyPropertyChanged
时,从后台线程设置属性不需要Dispatcher
。绑定引擎将把INotifyPropertyChanged.PropertyChanged
事件封送到正确的调度器线程(UI线程)。这不适用于
INotifyCollectionChanged.CollectionChanged
事件。您必须显式配置绑定引擎,以便通过使用BindingOperations.EnableCollectionSynchronization
方法将事件封送到调度器线程。这样,绑定引擎会在正确的调度器线程上引发INotifyCollectionChanged.CollectionChanged
事件:这样就消除了视图模型中最常见的
Dispatcher
用法(设置作为绑定源的属性,并向作为绑定源的集合添加项)。这将从代码中删除静态Singleton引用,以实现完全的可测试性。sxissh062#
您应该使用
IApplicationDispatcher
接口(选项2)。它不一定是构造函数参数。例如,您可以将其作为属性添加到基本视图模型类中,并在测试中根据需要进行设置。只要确保在真实的应用中将该属性设置为
Application.Current.Dispatcher
的 Package 器即可。我担心在视图模型中调用静态方法不会使它们对单元测试非常友好。
检查空引用(选项1)不是代码气味。你也应该这样做。