C# 如何单元测试WPF+;第三方物流
我正在运行一个简单的WPF应用程序,它使用基于事件的/TPL方法来处理数据。 本例中使用了三个类(视图、演示者、模型) WPF应用程序运行良好,但在测试中存在InvalidoperationException。我尝试通过调用C# 如何单元测试WPF+;第三方物流,c#,.net,wpf,unit-testing,task-parallel-library,C#,.net,Wpf,Unit Testing,Task Parallel Library,我正在运行一个简单的WPF应用程序,它使用基于事件的/TPL方法来处理数据。 本例中使用了三个类(视图、演示者、模型) WPF应用程序运行良好,但在测试中存在InvalidoperationException。我尝试通过调用 window.tb_test.Dispatcher.BeginInvoke((ThreadStart)delegate {window.tb_test.Text = t.ID + " " + t.Name}); 在UpdateTest中,但是在我的测试模块中没有调用“tb
window.tb_test.Dispatcher.BeginInvoke((ThreadStart)delegate {window.tb_test.Text = t.ID + " " + t.Name});
在UpdateTest中,但是在我的测试模块中没有调用“tb_test.textChanged”事件,尽管应用程序本身工作得很好。假设
窗口是一个WPF窗口,而tb_test
是一个WPFTextBlock
首先,让我先说一下,WPF线程模型使对活动WPF对象运行单元测试变得有点繁琐。就我个人而言,与处理所有这些问题的麻烦相比,我发现这些编码测试的好处微乎其微,尤其是在遵循MVVM设计模式时。将重要的逻辑移到更易于测试的位置(读取:数据绑定和操纵视图模型对象的命令)将使这些测试看起来更加冗余
如果你可以重新构造你的设计,这样你就可以在一个更容易测试的地方得到所有“重要”的逻辑,那么这就是我建议尝试的。我不知道这个代码库的历史是什么,所以我真的不想就此罢休。如果你不能,或者你只是好奇,那么让我们去兔子洞
当您在测试中设置当前的SynchronizationContext
时,您使用的是普通的SynchronizationContext
,它的Post
是使用ThreadPool
实现的(即,回调可以在任何线程上执行)。因此,当TaskScheduler.FromCurrentSynchronizationContext
去计划任务继续时,它有“更新TextBlock
的Text
”代码在可能是不同的线程上运行,必须在Dispatcher
线程”规则上执行WPF
如果Dispatcher
正在运行,您建议的使用Dispatcher.BeginInvoke
的修复可能会解决您眼前的问题。在发布的测试代码中,我看不到Dispatcher.Run
或Dispatcher.PushFrame
,因此我认为这有效地将在Dispatcher
上执行的任何操作都变成了no-op(进入一个永远无法读取的队列)。当应用程序正常运行时,Visual Studio自动为您生成的代码将调用应用程序。在可执行文件入口点的末尾运行
,最终调用调度程序。为您运行
,以便它可以开始处理诸如“显示主窗口”之类的消息
您可能会注意到,在调用Dispatcher.Run
之后,它会在您正在调用它的任何线程上阻止该调度器,直到您告诉它从另一个线程关闭为止。告诉它关闭后,无法在该线程上启动另一个Dispatcher
。。。因此,本质上,要么每个测试都需要向上旋转或向下旋转它自己的独立线程(如果您想编写更多的此类测试,至少对我来说,速度慢得令人烦恼),要么您可能会从使用花哨的[AssemblyInitialize]
/[AssemblyCleanup]中受益
MSTest的方法,这样您就可以为该项目中的所有测试只管理一个分派器
泵(这就是我们所做的)
一旦通过这些测试,您可能还会发现在测试中获取mwv.tb_test.Text
也需要在Dispatcher
线程上进行
您还面临竞争条件的风险,因为RaiseEvent
引发的事件可能(取决于您处理线程问题的方式)在TextChanged
处理程序连接到您的测试之前终止,这意味着ManualResetEvent
有时可能会永远阻塞,即使在其他所有操作之后。正如Joe的回答中所提到的,您需要一个在线程上运行的调度程序才能工作
有关单元测试中应该有效的代码,请参见以下答案:您的问题不太清楚。文本从哪里来?我看不出它已用于您的SUT(测试中的系统)。您想要测试什么“特定”场景,您的测试方法名称也丢失了。在你的SUT中,你有一个私有的void UpdateTest(Task任务),这个被称为“与处理所有这些问题的麻烦相比,我发现这些类型的编码测试的好处是最小的,特别是在遵循MVVM设计模式时。”首先,我想,在一个“真正的”WPF窗口中进行测试是一个好主意,而不仅仅是代码的逻辑部分。我将重新考虑结构+重构不好的部分。我很感激你们耐心地回答我的“新手”问题,也许还不太清楚。
[TestMethod]
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
waitHandle = new ManualResetEvent(false);
WPF.MainWindowView mwv = new MainWindowView();
mwv.btn_test.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent));
mwv.tb_test.TextChanged += (s, e) => waitHandle.Set();
waitHandle.WaitOne();
Assert.AreEqual("43 displayvalue", mwv.tb_test.Text);
window.tb_test.Dispatcher.BeginInvoke((ThreadStart)delegate {window.tb_test.Text = t.ID + " " + t.Name});