C# 测试WPF控件而不将其添加到窗口

C# 测试WPF控件而不将其添加到窗口,c#,wpf,testing,user-controls,C#,Wpf,Testing,User Controls,我有一个UserControl,它在其Loaded事件中发布EventAggregator消息。为了测试这一点(并引发Loaded事件),我当前正在创建一个窗口并向其中添加控件,然后等待引发Loaded事件 是否有任何方法可以设置测试,以便在不必创建控件并将其添加到窗口的情况下触发加载的事件 例如: [Test, RequiresSTA] public void active_thingy_message_is_published_on_loaded() { const string T

我有一个
UserControl
,它在其
Loaded
事件中发布
EventAggregator
消息。为了测试这一点(并引发
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
事件是在加载时触发的(这是您这样做集成测试的主要原因),那么您应该对此进行调查。

如果您只想触发目标控件的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方法,因此如果测试需要应用程序状态,这将不起作用


此外,父级的已加载事件与其子级之间没有关系。如果测试要求子元素触发其加载的事件,那么helper方法将需要手动遍历子控件并触发这些控件。

在过去两年中,情况可能发生了变化。出于代码覆盖率的考虑,我也遇到了这个问题,这是我的解决方案

WPF UIElements继承一个名为RaiseEvent的方法,该方法采用RoutedEventArgs。这可以使用特定UIElement的.LoadedEvent来构造,从而使您能够获得最后一点代码覆盖率


我怀疑你是否还需要我的答案,但可能有人需要。

你必须假装/模仿窗户。。。但是这看起来不再像单元测试了。@Henk你怎么看一个伪造的窗口导致我的用户控件被加载,并因此触发它的
loaded
事件?你可以尝试通过反射引发加载事件:@mike我怀疑WPF事件不是以同样的方式实现的。我无法使用链接中的方法大纲通过反射看到事件支持字段。已经尝试过这种方法,但将进一步挖掘。谢谢。@Merlyn我已经更新了示例单元测试。+1用于“如果您没有信心”的注释。有时候,如果一种方法学不能给你带来任何好处,那么最好不要武断地坚持它。在这种情况下,代码检查可能足以验证事件绑定。如果没有,则需要修复某个地方的问题。@Robert@Merlyn我已从标题和标记中删除了“Unit”,因此很明显,我想专门测试该问题,而不必陷入什么测试类别中。我一点也不教条。在加载的
事件中发布的消息对于生命周期以及消息如何与我无法控制的系统中的其他第三方组件交互非常重要。尽管挑战和剖析这是哪类测试很有诱惑力,但这并不能解决我的问题,因为这是一个有效且重要的测试用例。@Robert我完全相信加载的
事件将被触发-WPF框架会这样做!我不确信的是重构、代码更改等会破坏这段代码——我想要一个回归测试。如果此代码被破坏,则其他人很难诊断此问题。考试不及格是一个很好的解决办法。我想我理解你的意思。我相信答案是否定的。更准确地说,我相信虽然您最终可能会找到一种方法,在WPF应用程序的上下文之外创建此控件的实例,并启动其
加载的
事件,你为实现这一点所做的一切将破坏测试的可靠性。我希望不必进行反思,但就我而言,这没有任何复杂之处。