【问题标题】:How can I unit test something that uses VisualTreeHelper?如何对使用 VisualTreeHelper 的东西进行单元测试?
【发布时间】:2010-11-23 11:14:05
【问题描述】:

我有这个静态辅助函数:

    public static DependencyObject GetParentObject(DependencyObject child)
    {
        if (child == null) return null;
        ContentElement contentElement = child as ContentElement;

        if (contentElement != null)
        {
            var parent = ContentOperations.GetParent(contentElement);
            if (parent != null) return parent;

            var fce = contentElement as FrameworkContentElement;
            return fce != null ? fce.Parent : null;
        }

        //if it's not a ContentElement, rely on VisualTreeHelper
        return VisualTreeHelper.GetParent(child);
    }

它可以在实际应用程序中运行,但我正在尝试为它编写一些单元测试。这是我的第一次尝试:

    [Test]
    public void GetParentObject_returns_immediate_parent()
    {
        var contentControl = new ContentControl();
        var textBox = new TextBox();

        contentControl.BeginInit();
        contentControl.Content = textBox;
        contentControl.EndInit();

        var result = UIHelper.GetParentObject(textBox);
        Assert.AreSame(contentControl, result);
    }

不幸的是,它失败了,因为 VisualTreeHelper 返回 null。如何模拟一个可行的可视化树?

【问题讨论】:

    标签: wpf unit-testing visualtreehelper


    【解决方案1】:

    基于printing documents via Wpf-controls and convert to XPS 上的这个答案,我想出了以下扩展方法来创建可视化树。它在没有 STA 线程或任何东西的 NUnit 中运行良好。

    /// <summary>
    /// Render a UIElement such that the visual tree is generated, 
    /// without actually displaying the UIElement
    /// anywhere
    /// </summary>
    public static void CreateVisualTree(this UIElement element)
    {
        var fixedDoc = new FixedDocument();
        var pageContent = new PageContent();
        var fixedPage = new FixedPage();
        fixedPage.Children.Add(element);
        pageContent.ToMaybeOf<IAddChild>().Do(c => c.AddChild(fixedPage));
        fixedDoc.Pages.Add(pageContent);
    
        var f = new XpsSerializerFactory();
        var w = f.CreateSerializerWriter(new MemoryStream());
        w.Write(fixedDoc);
    }
    

    请注意

    • 另一个答案使用了一个看起来不像我看到的 API 的 Reach-dll API。我假设 .NET Framework 3.5 版和 4.0 版之间存在差异
    • ToMaybeOf 东西基本上意味着将pageContent 视为IAddChild 并在该界面上执行操作
    • 这将Window 类型的元素一起使用,因为该元素本质上是作为子元素添加到 Visual 中的,而 Window 会对此抱怨不已。

    【讨论】:

    • 我将pageContent转换为IAddChild并直接执行操作,而不是依赖ToMaybeOfDo((IAddChild)pageContent).AddChild(fixedPage);
    【解决方案2】:

    这就是为什么静态是有问题的。

    您可以抽象接口背后的功能并创建使用静态方法的默认实现。然后,您可以使用依赖注入,这使得该单元测试变得微不足道——模拟对 IVisualTreeHelper 的依赖或滚动您自己的存根实现,您可以将其配置为返回您分配的任何值。

    public class Foo
    {
        static IVisualTreeHelper visualTreeHelper;
    
        static Foo()
        {
            Foo.visualTreeHelper = new FrameworkVisualTreeHelper();
        }
    
        public Foo(IVisualTreeHelper visualTreeHelper)
        {
            Foo.visualTreeHelper = visualTreeHelper;
        }
    
        public static DependencyObject GetParentObject(DependencyObject child)
       {
           if (child == null) return null;
           ContentElement contentElement = child as ContentElement;
    
           if (contentElement != null)
           {
               var parent = ContentOperations.GetParent(contentElement);
               if (parent != null) return parent;
    
               var fce = contentElement as FrameworkContentElement;
               return fce != null ? fce.Parent : null;
           }
    
           //if it's not a ContentElement, rely on the IVisualTreeHelper
           return visualTreeHelper.GetParent(child);
       }
    }
    
    public interface IVisualTreeHelper
    {
        DependencyObject GetParent(DependencyObject reference);
    }
    
    public class FrameworkVisualTreeHelper : IVisualTreeHelper
    {
        public DependencyObject GetParent(DependencyObject reference)
        {
            return VisualTreeHelper.GetParent(reference);
        }
    }
    

    显然,如果您在别处使用其他方法,您可能需要将其他 VisualTreeHelper 方法添加到您的接口和默认实现中。

    它仍然不完全干净,因为您正在测试的单元本身是静态的,当您尝试对依赖于 UIHelper 类的静态方法的任何类进行单元测试时,您将遇到完全相同的问题。

    【讨论】:

      【解决方案3】:

      要模拟一棵可视化树,您必须实际创建并渲染一个。所以你必须创建一个实际的窗口,这对于单元测试来说并不是特别理想。

      【讨论】:

        猜你喜欢
        • 2017-07-10
        • 2018-12-20
        • 2020-04-07
        • 1970-01-01
        • 1970-01-01
        • 2012-02-20
        • 2012-01-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多