【问题标题】:Errors mocking TreeView in FakeItEasy在 FakeItEasy 中模拟 TreeView 时出错
【发布时间】:2014-11-03 10:31:40
【问题描述】:

使用 FakeItEasy 和 xBehave.net,我正在尝试模拟 System.Windows.Forms.TreeView

我收到以下错误:

FakeItEasy.Core.FakeCreationException : Failed to create fake of type "System.Windows.Forms.TreeView".

  Below is a list of reasons for failure per attempted constructor:
    No constructor arguments failed:
      No usable default constructor was found on the type System.Windows.Forms.TreeView.
      An exception was caught during this call. Its message was:
      Exception has been thrown by the target of an invocation.

这让我很困惑,因为文档中唯一的 constructor I see 是一个公共的默认构造函数。

这是给出错误的演示代码:

using System.Windows.Forms;
using Xbehave;
using FakeItEasy;

namespace MockingTreeView
{
    public class Class1
    {
        TreeView treeView;

        [Scenario]
        public void MockingTreeView()
        {
            "Given there is a treeView".f(() =>
            {
                // Apparently UIPermissionAttribute can't be mocked by this framework
                Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(System.Security.Permissions.UIPermissionAttribute));
                treeView = A.Fake<TreeView>();
            });
        }
    }
}

有谁知道出了什么问题或如何解决这个问题?谢谢。

【问题讨论】:

    标签: c# treeview fakeiteasy


    【解决方案1】:

    不幸的是,某些类(我之前在 WinForms 系列中已经注意到)很难伪造。就个人而言,我更喜欢在可能的情况下伪造一个定义良好的接口(或者很少是抽象类)。除了难以获得假货之外,有时现有的类(例如TreeView)具有large surface area 和复杂的内部行为,可能会让您大吃一惊。

    话虽如此,FakeItEasy 有时会尝试提供有用、友好的错误消息。在这种情况下,乐于助人的本能最终掩盖了正在发生的事情。我们可能应该调查一下。

    您对可用构造函数的困惑是可以理解的,但错误消息中的关键词是usable。 FakeItEasy 找到了默认的构造函数,但是因为它抛出了异常而无法使用。

    我接受了您的测试,并针对我可以调试的 FakeItEasy 构建运行它,并在发现异常时停止。这是一个System.Reflection.TargetInvocationException,所以这不是特别有用,但内部异常看起来像:

    [System.NullReferenceException]
    Message: "Object reference not set to an instance of an object."
    StackTrace: 
       at Castle.Proxies.TreeViewProxy.get_DefaultMargin()
       at System.Windows.Forms.Control..ctor(Boolean autoInstallSyncContext)
       at System.Windows.Forms.Control..ctor()
       at System.Windows.Forms.TreeView..ctor()
       at Castle.Proxies.TreeViewProxy..ctor(IInterceptor[] )
    

    这告诉我,TreeView 的构造函数最终调用了 Control 构造函数,该构造函数调用了 DefaultMargin 的 get 方面,这是一个受保护的属性。 因为它是受保护的,所以 FakeItEasy 看不到它,所以在 Control 上调用了原始方法。它看起来像这样:

    /// <include file="doc\Control.uex" path="docs/doc[@for="Control.DefaultMargin"]/*">
    protected virtual Padding DefaultMargin {
        get { return CommonProperties.DefaultMargin; }
    }
    

    (来自Control.cs source code in C# .NET

    我不完全确定为什么会抛出 NullReferenceException

    这就是它失败的原因(在某种程度上)。

    这不是一个令人满意的结论,但我会尽量不要伪造 TreeView。我会寻找我拥有和控制的可以伪造的东西,通常是一些易于使用的接口,由使用 TreeView 的类实现。

    【讨论】:

    • 感谢您的深入了解。我是新手,但听起来像是在监视?由于调用了实现。我正在尝试对特定方法进行单元测试,并且需要查看它在 TreeView 对象上调用了哪些添加/删除方法。我想我可以包装 TreeView。
    • 你的意思是像 Test Double 类型 Spy 那样进行间谍活动?这种区别我不太在意,但是FIE假货确实记录了它们是如何被调用的。这里最重要的是 FIE 使用 DynamicProxy 来创建假货,这意味着每个假货本质上都是假货类型的子类。这就是为什么方法必须是公共的和虚拟的才能配置,以及为什么要调用基构造函数。接口没有实现,所以没有包袱,使用起来更容易……
    • 我的意思是像this post 那样进行间谍活动,“你获取一个现有对象并仅“替换”一些方法”。我明白你在说什么。
    • 我以前从未遇到过这个定义。我想知道它是模仿特定的。不管。我会说大多数 FakeItEasy 赝品都不是这种意义上的间谍。 FIE 是一个全新的对象,是凭空创建的,即使它是扩展现有类型的类型的对象。通过(非常)粗略地阅读 mockito 文档/代码,我认为 mockito 间谍大致相当于使用 wrapping creation option: var wrapped = new Foo(); var foo = A.Fake&lt;IFoo&gt;(x =&gt; x.wrapping(wrapped)); 创建 FIE 假货
    • 因此,FIE fakes 是凭空创建的,但仍会调用构造函数。我看到there is a GetUninitializedObject() call 在不调用构造函数的情况下创建对象。我希望我可以使用包装创建选项。 wrapped 对象已创建,但在调用 A.Fake() 时出现相同的错误。如果这在技术上是可行的,那么在 FIE 中有一个“.Uninitialized()”选项会很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多