Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何单元测试WPF+;第三方物流_C#_.net_Wpf_Unit Testing_Task Parallel Library - Fatal编程技术网

C# 如何单元测试WPF+;第三方物流

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

我正在运行一个简单的WPF应用程序,它使用基于事件的/TPL方法来处理数据。 本例中使用了三个类(视图、演示者、模型)

WPF应用程序运行良好,但在测试中存在InvalidoperationException。我尝试通过调用

window.tb_test.Dispatcher.BeginInvoke((ThreadStart)delegate {window.tb_test.Text = t.ID + " " + t.Name});

在UpdateTest中,但是在我的测试模块中没有调用“tb_test.textChanged”事件,尽管应用程序本身工作得很好。

假设
窗口是一个WPF
窗口,而
tb_test
是一个WPF
TextBlock


首先,让我先说一下,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});