针对WPF和调度员的NMOCK问题测试
-
30-09-2019 - |
题
这是一个穿线瘾君子的人。我有这样的方法:
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);
});
}
});
}
这是在WPF用户控件中定义的。 Meleltsavailable是Meltdtos的观测值。在应用程序本身运行时,此代码可正常工作。
问题在于,我想创建一个使用nmock的单元测试来验证该方法的结果 - 具体来说,一旦被称为,Meleltsavailable属性就有一些项目。这是测试方法:
[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");
}
该测试在第一个断言上始终失败。 vm.融化的瓦利布尔当时是空的。
如果我剥离所有螺纹并将其放置为:
public void RefreshMelts()
{
MeltsAvailable.Clear();
IList<MeltDto> meltDtos = meltingAppService.GetActiveMelts();
foreach (MeltDto availableMelt in meltDtos)
{
MeltsAvailable.Add(availableMelt);
}
OnPropertyChanged("MeltsAvailable");
}
测试通过。
因此,显然,线程不喜欢某些内容 - 但是即使打开debug-> exceptions-> clr异常 - >抛出,并仅关闭我的代码,我根本没有例外。
最奇怪的部分是调度员。我将熔体对象加载到熔融杂货集合中似乎从未被调用。我可以用断点覆盖整个部分,而且它们永远不会受到打击。在我的测试中提高线程的时间,甚至十秒钟都没有改变。
为什么?为什么该部分不执行,为什么我不能介入它或闯入它,为什么我不遇到异常,为什么它在执行方面效果很好,但在测试中不行?
非常感谢,史蒂夫
解决方案
调度程序是与执行线程相关的消息循环。当主线程空闲时,它会处理其队列中的项目。在单位测试中,这永远不会发生。线程很忙,然后在测试完成时退出。
如果您使用Visual Studio来运行测试,则可以打开代码覆盖范围的突出显示,并且您会看到dispatcher.invoke()中的代码永远不会被调用(它将以红色显示)。
可以使用调度器框架触发调度程序来处理排队的消息。将以下帮手类添加到您的单元测试项目:
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;
}
}
在测试结束时(断言之前)致电dispatcherHelper.doevents()。这将触发调度员处理杰出的事件,例如将项目添加到视图模型的可观察到的集合中的事件。然后,您可以检查视图模型的属性,以验证它们是否正确设置。