我考虑将此作为另一个问题放在这个帖子中:
Unit testing with MVVM Light & DispatcherHelper
但是当我深入研究它时,我认为它有点不同。
我有一个包含视图模型/视图的项目,一个包含模型的单独项目,以及包含模型测试的第三个单独的单元测试项目(MSTest)。我正在使用MVVM Light的DispatcherHelper来帮助我在后台任务中工作,但是要将错误从该任务返回到UI。
视图模型/视图项目具有App.xaml.cs文件,该文件包含OnStartup事件处理程序,我在其中放置了DispatcherHelper.Initialize()调用。其中一个VM使用delegate.BeginInvoke()在另一个线程上调用一个模型的长期运行方法:
FileProcessDelegate processor = GenerateAddressingFile;
processor.BeginInvoke(FileGenerationComplete, null);
在这个长时间运行的方法中,它调用实用程序方法将错误发送回UI。这些是使用DispatcherHelper.CheckBeginInvokeOnUI()
发送的mvvm-light消息的形式 internal static void ReportErrorToUi(IFileError error)
{
DispatcherHelper.CheckBeginInvokeOnUI(
() => Messenger.Default.Send(new GenericMessage<IFileError>(error), MessageTokens.ReportFileError));
}
这一切似乎在应用程序中运行良好。
在模型的其中一个单元测试类中,我尝试检查长时间运行的方法是否在发生错误时正确发送消息。在该测试类中,我在MSTest类初始化方法中调用了DispatcherHelper.Initialize()。
#region Additional test attributes
//
//You can use the following additional attributes as you write your tests:
//
//Use ClassInitialize to run code before running the first test in the class
[ClassInitialize]
public static void MyClassInitialize(TestContext testContext)
{
DispatcherHelper.Initialize();
}
测试注册应该发送的消息,然后直接调用模型的方法,而不在单独的线程上调用它。据我了解,因为发送消息的调用由模型中的DispatcherHelper.CheckBeginInvokeOnUI()包装,无论是在后台线程上还是在UI线程上发生,都应该做正确的事情。
在这个检查方法的类的第一个测试中,一切正常。该测试接收发回的错误消息,并以漂亮的颜色传递。
当同一类中的后续测试运行时,测试永远不会收到消息。当我在后续测试中逐步完成它时,我注意到当我到达DispatcherHelper.CheckBeginInvokeOnUI()方法时,DispatcherHelper.UIDispatcher.Thread实例表明它已经死了。
有什么想法吗?
答案 0 :(得分:3)
我不确定这个解决方案是否可以包含在Mvvm Light中,或者它是否违反了框架的一些基本前提,但是我这样做是为了让它工作:
我创建了DispatcherHelper类的自定义本地版本,它与Mvvm Light中的内容略有不同,即Initialize()方法中的逻辑:
public static void Initialize()
{
if (UIDispatcher != null &&
UIDispatcher.Thread.IsAlive)
{
return;
}
UIDispatcher = Dispatcher.CurrentDispatcher;
}
此处,更改是添加:
&& UIDispatcher.Thread.IsAlive
条件。这允许我在单元测试类的MyTestInitialize()方法中调用DispatcherHelper.Initialize()。这样,如果与UIDispatcher关联的先前线程已经死亡,则DispatcherHelper将获取将在其上运行此当前测试方法的新Dispatcher / Thread。
显然,在VSTS测试框架中,没有一个“中心”线程就像应用程序中的UI线程一样。 MyClassInitialize()方法在一个线程上执行,然后测试在一个完全不同的线程上运行。 Mvvm Light定义不允许我通过再次调用DispatcherHelper.Initialize()来将DispatcherHelper挂钩到新的测试线程,因为UIDispatcher不是null,它的线程刚刚死了。
答案 1 :(得分:0)
测试异步代码很棘手。通常情况下,如果它工作一次,然后不再工作,听起来好像你没有正确地同步你的线程。在你的测试方法(主线程)中,你在等待后台线程执行吗?
Jonas Follesoe在Silverlight中写了一些关于异步单元测试的好文章,例如http://jonas.follesoe.no/2007/09/18/unit-testing-event-based-asynchronous-code/。这通常是我对异步代码进行单元测试的方式。
希望这有帮助, 劳伦