代码之家  ›  专栏  ›  技术社区  ›  Scott Whitlock

如何对使用VisualTreeHelper的内容进行单元测试?

  •  8
  • Scott Whitlock  · 技术社区  · 14 年前

    我有一个静态助手函数:

        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 正在返回空值。我怎样才能模拟出一个可视的树来工作呢?

    3 回复  |  直到 14 年前
        1
  •  2
  •   Jay    14 年前

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

    您可以抽象接口背后的功能,并创建使用静态方法的默认实现。然后,您可以使用依赖注入,这使得这个单元测试变得简单——模拟对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类的静态方法的任何类进行单元测试时,您将遇到完全相同的问题。

        2
  •  3
  •   Community CDub    7 年前

    基于这个答案 printing documents via Wpf-controls and convert to XPS 我提出了以下扩展方法来创建可视化树。它在努尼特内部工作良好,没有斯塔线或任何东西。

    /// <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);
    }
    

    请注意

    • 另一个答案是使用Reach dll的API,它看起来不像我看到的API。我假设.NEt Framework 3.5和4.0版本之间存在差异
    • 这个 ToMaybeOf 基本上就是治疗 pageContent 作为 IAddChild 在那个界面上做一个动作
    • 这个遗嘱 使用类型为的元素 窗口 因为元素本质上是作为一个孩子添加到一个视觉和窗口将对此怨声载道。
        3
  •  -1
  •   bitbonk    14 年前

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