在不将其添加到窗口的情况下测试WPF控件

Tim*_*oyd 12 c# testing wpf user-controls

我有一个在其活动中UserControl发布EventAggregator消息Loaded.为了测试这个(并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; }
}
Run Code Online (Sandbox Code Playgroud)

更新

我已将标题从"单元测试..."更改为"测试...",并将标记"单元测试"替换为"测试".

我宁愿不分开究竟是什么类别的测试,因为它不具有建设性.是的,可以说这不是"单元测试",但这没有用.我想测试一个依赖于控件生命周期的问题,这涉及到Loaded事件.这是一个重要的回归测试,因为我无法控制的第三方组件取决于所提出的消息Loaded.

可以在Loaded不将控件添加到窗口的情况下引发事件吗?

Mag*_*gus 8

在过去的两年里,事情可能发生了变化.为了代码覆盖,我也遇到了这个问题,这是解决方案.

WPF UIElements继承了一个名为RaiseEvent的方法,它接受一个RoutedEventArgs.这可以使用特定的UIElement的.LoadedEvent构建,允许您获得代码覆盖的最后一位.

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


Pau*_*lls 7

如果您只对触发目标控件的Loaded事件感兴趣,那么Reflection应该可以解决问题.

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 });
}
Run Code Online (Sandbox Code Playgroud)

这实际上触发了每个FrameworkElement中存在的OnLoaded方法,因此如果您的测试需要Application状态,那么这将不起作用.

此外,父母的Loaded事件与其子女之间没有任何关系.如果测试需要子元素触发其Loaded事件,则helper方法将需要手动遍历子控件并触发它们.


Rob*_*ney 5

Loaded事件处理程序中的内容重构为自己的方法,并让Loaded事件处理程序调用它.编写单元测试来测试重构方法,而不是Loaded事件.

单元测试是验证一个单元是否完成它应该做的事情.测试单元如何与其他单元互操作是集成测试.进行集成测试以及能够对集成单元进行回归测试当然很有价值,但这与单元测试不同.

最后:如果你没有信心控件的Loaded事件在加载时被触发(这是你进行这样的集成测试的主要原因),那就是非常错误,你应该调查一下.

  • 我想我理解你的观点.我相信答案是否定的.更确切地说,我相信虽然你最终可能会找到一种在WPF应用程序的上下文之外创建这个控件实例并使其"加载"事件触发的方法,但是你将要做的就是破坏可靠性测试. (2认同)