测试 WPF 控件而不将其添加到窗口
Posted
技术标签:
【中文标题】测试 WPF 控件而不将其添加到窗口【英文标题】:Testing WPF Control Without Adding it to a Window 【发布时间】:2011-10-30 23:44:49 【问题描述】:我有一个UserControl
,它在其Loaded
事件中发布EventAggregator
消息。为了对此进行测试(并引发Loaded
事件),我目前正在创建一个窗口并向其添加控件,然后等待引发Loaded
事件。
有没有什么方法可以设置一个测试,以便触发Loaded
事件而无需创建控件并将其添加到窗口?
例如:
[Test, RequiresSTA]
public void active_thingy_message_is_published_on_loaded()
const string TestMsg = "Active thingy changed";
using (AutoResetEvent loadedEvent = new AutoResetEvent(false))
DummyEventService eventService = new DummyEventService();
DummyControl control = new DummyControl(eventService, TestMsg);
control.Loaded += delegate loadedEvent.Set(); ;
Assert.That(eventService.Message, Is.Null, "Before.");
Window window = new Window Content = control ;
window.Show();
loadedEvent.WaitOne();
window.Dispatcher.InvokeShutdown();
Assert.That(eventService.Message, Is.EqualTo(TestMsg), "After.");
private class DummyControl : UserControl
public DummyControl(DummyEventService eventService, string testMsg)
Loaded += delegate eventService.Publish(testMsg); ;
private class DummyEventService
public string Message get; private set;
public void Publish(string msg) Message = msg;
更新
我已将标题从“单元测试...”更改为“测试...”,并将标签“单元测试”替换为“测试”。
我不希望就这到底是什么类别的测试而分道扬镳,因为它没有建设性。是的,可以说这不是“单元测试”,但这没有帮助。我想测试一个依赖于控件生命周期的问题,这涉及Loaded
事件。这是一个重要的回归测试,因为我无法控制的第 3 方组件取决于在 Loaded
提出的消息。
可以在不将控件添加到窗口的情况下引发Loaded
事件吗?
【问题讨论】:
你将不得不伪造/模拟窗口......但这看起来不再像单元测试了。 @Henk 你如何看到一个伪造的窗口导致我的用户控件被加载,从而触发它的Loaded
事件?
您可以尝试通过反射引发 Load 事件:***.com/questions/198543/…
@mike 我怀疑 WPF 事件的实现方式不同。我无法使用链接中的方法大纲通过反射看到事件支持字段。已经尝试过这种方法,但会进一步挖掘。谢谢。
@Merlyn 我已经更新了示例单元测试。
【参考方案1】:
如果您只是对触发目标控件的 Loaded 事件感兴趣,那么反射应该可以解决问题。
public static void RaiseLoadedEvent(FrameworkElement element)
MethodInfo eventMethod = typeof(FrameworkElement).GetMethod("OnLoaded",
BindingFlags.Instance | BindingFlags.NonPublic);
RoutedEventArgs args = new RoutedEventArgs(FrameworkElement.LoadedEvent);
eventMethod.Invoke(element, new object[] args );
这实际上会触发每个 FrameworkElement 中存在的 OnLoaded 方法,因此如果您的测试需要应用程序状态,这将不起作用。
此外,父级的 Loaded 事件与其子级之间没有关系。如果测试要求子元素触发其 Loaded 事件,则辅助方法将需要手动遍历子控件并触发它们。
【讨论】:
+1 我希望不必求助于反思,但就我而言,这并不复杂。【参考方案2】:在过去的两年里,也许情况发生了变化。为了代码覆盖率,我也遇到了这个问题,它的解决方案。
WPF UIElements 继承了一个名为 RaiseEvent 的方法,该方法接受一个 RoutedEventArgs。这可以使用特定的 UIElement 的 .LoadedEvent 构建,让您获得最后的代码覆盖率。
我怀疑你仍然需要我的回答,但有人可能会。
【讨论】:
【参考方案3】:将Loaded
事件处理程序中的内容重构为它自己的方法,并让Loaded
事件处理程序调用它。编写单元测试来测试重构的方法,而不是 Loaded
事件。
单元测试是验证一个单元是否完成了它应该做的事情。测试单元如何与其他单元互操作是集成测试。进行集成测试当然很有价值,并且能够对集成单元进行回归测试,但这是与单元测试不同的任务。
最后:如果您不确定控件的 Loaded
事件在加载时会被触发(这是您进行此类集成测试的主要原因),那么您应该进行调查.
【讨论】:
+1 表示“如果您没有信心”的评论。有时最好不要教条地坚持一种方法,如果它没有给你带来任何好处。在这种情况下,代码审查可能足以验证事件绑定。 如果不是,则说明某处有问题需要解决。 @Robert @Merlyn 我已经从标题和标签中删除了“Unit”,因此很明显我想专门测试这个问题,而不会陷入它属于哪类测试的困境。我一点也不教条。在Loaded
事件中发布的消息在生命周期方面非常重要,以及消息如何与我没有 控制的系统中的其他第 3 方组件一起播放。尽管挑战和剖析这是什么类别的测试很诱人,但它并不能解决我的问题,这是一个有效且重要的测试用例。
@Robert 我完全有信心Loaded
事件将被触发——WPF 框架就是这样做的!我不确定的是重构、代码更改等会破坏这段代码——我想要一个回归测试。如果此代码被破坏,其他人就很难诊断出问题。失败的测试是解决此问题的绝佳方法。
我想我明白你的意思了。我相信答案是否定的。更准确地说,我相信虽然您最终可能会找到一种在 WPF 应用程序上下文之外创建此控件的实例并触发其Loaded
事件的方法,但您为使其成为可能所做的事情将破坏可靠性测试。以上是关于测试 WPF 控件而不将其添加到窗口的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI:将动画添加到 Picker 而不将其添加到值中
将文件推送到 Github/Gitlab 而不将其添加到 .git?
我可以在设备上调试我的 iOS 应用程序而不将其添加到 Apple 配置门户吗?