我正在尝试按照Using the WPF Dispatcher in unit tests中的建议来运行我的nUnit测试。
当我按照下面的方式编写单元测试时,它可以工作:
[Test]
public void Data_Should_Contain_Items()
{
DispatcherFrame frame = new DispatcherFrame();
PropertyChangedEventHandler waitForModelHandler = delegate(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Data")
{
frame.Continue = false;
}
};
_myViewModel.PropertyChanged += waitForModelHandler;
Dispatcher.PushFrame(frame);
Assert.IsTrue(_myViewModel.Data.Count > 0, "Data item counts do not match");
}
但是,如果我尝试使用DispatcherUtil的建议,它就不起作用:
[Test]
public void Data_Should_Contain_Items()
{
DispatcherUtil.DoEvents();
Assert.IsTrue(_myViewModel.Data.Count > 0, "Data item counts do not match");
}
public static class DispatcherUtil
{
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
private static object ExitFrame(object frame)
{
((DispatcherFrame)frame).Continue = false;
return null;
}
}
当我使用DispatcherUtil时,看起来在数据准备好之前,对ExitFrame的调用太快了。
我没有正确使用DispatcherUtil吗?这似乎是一种更好的方法来处理调度程序,而不是等待视图模型的回调。
答案 0 :(得分:7)
由于调度程序在单元测试中存在问题,我的解决方案是破坏视图模型对调度程序的依赖性。我假设你目前有硬编码的引用,如:
Dispatcher.CurrentDispatcher.BeginInvoke(..
调度程序是外部依赖项,不应该是单元测试的一部分 - 我们可以假设调度程序有效。
我会使用依赖注入(可能是穷人,Unity等)。 创建一个代表调度程序的合适接口。 创建一个真正的实现,包装真正的调度程序。 创建一个使用Action.BeginInvoke的虚假实现。 在假冒中你记录了所有返回到BeginInvoke调用的IAsyncResults 然后有一个帮助方法,等待所有完成的调用,你可以在测试中使用等待完成。
或者有一个视图模型基类,它做同样的事情。正常调用调度程序,但可以在测试期间指示调用伪造。
答案 1 :(得分:2)
我找到了一个简单的解决方案。我没有在Dispatcher中处理Frames Async,而是在DispatcherUtil类中添加了一个同步方法。调用此DoEventsSync() - 方法在处理所有帧时返回,我认为这应该有帮助:
public static class DispatcherUtil
{
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void DoEvents()
{
var frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
public static void DoEventsSync()
{
var frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
private static object ExitFrame(object frame)
{
((DispatcherFrame)frame).Continue = false;
return null;
}
}
现在,只需在单元测试中使用DispatcherUtil.DoEventsSync();
代替DispatcherUtil.DoEvents();
。在单元测试继续之前,您可以确保Dispatcher处理了所有内容。不需要为测试添加回调。
令人困惑的部分是DispatcherUtil.DoEvents();
确实是DispatcherUtil.DoEventsAsync();
因为BeginInvoke(..)
是异步方法