Ste*_*ith 2 c# testing wpf dispatcher nmock
这是一个针对线程迷的人.我有这个方法:
public void RefreshMelts()
{
MeltsAvailable.Clear();
ThreadPool.QueueUserWorkItem(delegate
{
Dispatcher.BeginInvoke((ThreadStart)delegate
{
eventAggregator.GetEvent<BusyEvent>().Publish(true);
eventAggregator.GetEvent<StatusMessageEvent>().Publish(
new StatusMessage("Loading melts...", MessageSeverity.Low));
});
try
{
IList<MeltDto> meltDtos = meltingAppService.GetActiveMelts();
Dispatcher.Invoke((ThreadStart)delegate
{
foreach (MeltDto availableMelt in meltDtos)
{
MeltsAvailable.Add(availableMelt);
}
OnPropertyChanged("MeltsAvailable");
eventAggregator.GetEvent<BusyEvent>().Publish(false);
eventAggregator.GetEvent<StatusMessageEvent>().Publish(
new StatusMessage("Melts loaded", MessageSeverity.Low));
});
}
catch (ApplicationException ex)
{
log.Error("An error occurred in MeltsViewModel when attempting to load melts", ex);
Dispatcher.Invoke((ThreadStart)delegate
{
MeltsAvailable.Clear();
eventAggregator.GetEvent<StatusMessageEvent>().Publish(
new StatusMessage("Melt data could not be loaded because an error occurred; " +
"see the application log for detail",
MessageSeverity.High));
eventAggregator.GetEvent<BusyEvent>().Publish(false);
});
}
});
}
Run Code Online (Sandbox Code Playgroud)
这是在WPF用户控件中定义的.MeltsAvailable是MeltDtos的ObservableCollection.在应用程序本身中运行时,此代码可以很好地工作.
问题是我想使用NMock创建一个单元测试来验证这个方法的结果 - 具体来说,一旦调用它,MeltsAvailable属性就有了一些项目.这是测试方法:
[TestMethod]
public void GetAvailableMeltsTest()
{
MeltDto mockMelt1 = new MeltDto();
MeltDto mockMelt2 = new MeltDto();
mockMelt1.MeltIdentifier = "TST0001";
mockMelt2.MeltIdentifier = "TST0002";
IList<MeltDto> availableMelts = new List<MeltDto>();
availableMelts.Add(mockMelt1);
availableMelts.Add(mockMelt2);
Expect.Exactly(1).On(service).Method("GetActiveMelts").Will(Return.Value(availableMelts));
MeltsViewModel vm = new MeltsViewModel(aggregator, logger, service, configManagerFactory); // All of these are mock objects
vm.RefreshMelts();
Thread.Sleep(millisecondDelayForEventPublish * 100);
mockery.VerifyAllExpectationsHaveBeenMet();
Assert.AreEqual(vm.MeltsAvailable.Count, 2);
Assert.AreEqual(vm.MeltsAvailable[0].MeltIdentifier, "TST0001");
Assert.AreEqual(vm.MeltsAvailable[1].MeltIdentifier, "TST0002");
}
Run Code Online (Sandbox Code Playgroud)
第一个Assert.AreEqual上的测试始终失败.vm.MeltsAvailable在那时是空的.
如果我删除所有线程并将其保留为:
public void RefreshMelts()
{
MeltsAvailable.Clear();
IList<MeltDto> meltDtos = meltingAppService.GetActiveMelts();
foreach (MeltDto availableMelt in meltDtos)
{
MeltsAvailable.Add(availableMelt);
}
OnPropertyChanged("MeltsAvailable");
}
Run Code Online (Sandbox Code Playgroud)
测试通过.
所以,显然,它有一些与线程无关的东西 - 但是甚至打开Debug-> Exceptions-> CLR Exceptions-> Thrown,并关闭Just My Code,我在RefreshMelts中完全没有例外.
最奇怪的部分是Dispatcher.Invoke调用,我将MeltDto对象加载到MeltsAvailable集合中似乎永远不会被调用.我可以用断点覆盖整个部分,它们永远不会被击中.提升Thread.Sepep在我的测试中的时间甚至高达十秒也没有任何变化.
为什么?为什么那部分没有执行,为什么我不能介入它或闯入它,为什么我没有得到异常,为什么它在执行中工作得很好而在测试中却没有?
非常感谢,史蒂夫
小智 5
Dispatcher是一个与执行线程相关联的消息循环.当主线程空闲时,它处理队列中的项目.在单元测试中,这种情况从未发生过.线程忙,然后在测试完成后退出.
如果您使用Visual Studio运行测试,则可以启用代码覆盖突出显示,并且您将看到Dispatcher.Invoke()中的代码永远不会被调用(它将以红色显示).
DispatcherFrame可用于触发Dispatcher处理排队的消息.将以下帮助程序类添加到单元测试项目中:
public static class DispatcherHelper
{
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;
}
}
Run Code Online (Sandbox Code Playgroud)
在测试结束时(断言之前)调用DispatcherHelper.DoEvents().这将触发Dispatcher处理未完成的事件,例如将项添加到视图模型的可观察集合中的事件.然后,您可以检查视图模型的属性以验证它们是否已正确设置.