10

我有这个静态辅助函数:

    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。我如何模拟一个可以工作的可视化树?

4

3 回答 3

3

Based on this answer here on printing documents via Wpf-controls and convert to XPS I came up with the following extension method to create the visual tree. It works well within NUnit without STA-thread or anything.

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

Please note that

  • the other answer uses an API of the Reach-dll that does not look like the API I am seeing. I assume that there are differences between .NEt Framework versions 3.5 and 4.0
  • the ToMaybeOf stuff basically means to treat pageContent as IAddChild and do an action on that interface
  • this will not work with an element of type Window since the element is essentially added as a child to a Visual and Window will complain bitterly about this.
于 2012-03-01T15:58:08.890 回答
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 类的静态方法的任何类进行单元测试时,您将遇到完全相同的问题。

于 2010-11-23T14:38:10.880 回答
-1

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

于 2010-11-23T14:22:18.070 回答