2

Using FakeItEasy and xBehave.net, I'm trying to mock a System.Windows.Forms.TreeView.

I get the following error:

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.

This confuses me because the only constructor I see in the docs is a public, default constructor.

Here's demo code that gives the error:

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

Does anyone know what's going wrong or how to troubleshoot this? Thanks.

Adam Ralph
  • 29,453
  • 4
  • 60
  • 67
Vimes
  • 10,577
  • 17
  • 66
  • 86

1 Answers1

2

Unfortunately what's going on is that some classes, and I've noticed this before with the WinForms family, are very difficult to fake. Personally, I prefer to fake a well-defined interface (or rarely an abstract class) when it's at all possible. Aside from difficulties in even getting the fake, sometimes existing classes such as TreeView have a large surface area and complex internal behaviour that can surprise you.

Anyhow, that being said, FakeItEasy sometimes tries to provide helpful, friendly error messages. In this case, the helpful instinct ends up obscuring what's going on. We should probably look into that.

Your confusion over the available constructors is understandable, but the key word in the error message is usable. FakeItEasy found the default constructor, but it wasn't usable because it threw an exception.

I took your test and ran it against a FakeItEasy build that I could debug into and stopped at the point the exception was found. It's a System.Reflection.TargetInvocationException, so that's not particularly useful, but the inner exception looks like:

[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[] )

This says to me that the TreeView's constructor ultimately calls a Control constructor that calls the get aspect of DefaultMargin, which is a protected property. Because it's protected, FakeItEasy can't see it, and so the original method is called on Control. It seems to look like this:

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

(from Control.cs source code in C# .NET)

I'm not entirely sure why that's throwing a NullReferenceException.

So that's (some of the way toward) why it's failing.

This isn't a satisfying conclusion, but me, I'd try not to fake the TreeView. I'd look for something that I own and control that I could fake, typically some easy-to-work-with interface that's implemented by a class that uses a TreeView.

Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • Thanks for diggin in. I'm new to this, but it sounds like it's spying? Since implementation is called. I'm trying to unit test a particular method and need to see which add/remove methods it's calling on the TreeView object. I suppose I could wrap the TreeView. – Vimes Sep 10 '14 at 15:52
  • You mean spying like the Test Double type [Spy](http://www.martinfowler.com/bliki/TestDouble.html)? I don't pay too much attention to that kind of distinction, but FIE fakes _do_ record how they are called. The big thing here is that FIE uses DynamicProxy to create fakes, and this means that each fake is essentially a subclass of the faked type. That's why methods have to be public and virtual to be configured, and why base constructors are called. Interfaces have no implementation, so bring no baggage and are much easier to work with… – Blair Conrad Sep 10 '14 at 19:18
  • I meant spying like in [this post](http://stackoverflow.com/questions/12827580/mocking-vs-spying-in-mocking-frameworks) where, 'you take an existing object and "replace" only some methods'. I understand what you're saying. – Vimes Sep 10 '14 at 22:28
  • I've never encountered that definition before. I wonder it's mockito-specific. No matter. I'd say that most of the FakeItEasy fakes are not spies in this sense. A FIE is a whole new object, created out of thin air, even if it is an object of a type that extends an existing type. From a (very) cursory reading of the mockito docs/code, I think a mockito spy is roughly equivalent to creating a FIE fake using the [wrapping creation option](https://github.com/FakeItEasy/FakeItEasy/wiki/Creating-Fakes#options): `var wrapped = new Foo(); var foo = A.Fake(x => x.wrapping(wrapped));` – Blair Conrad Sep 11 '14 at 01:01
  • So, FIE fakes are created out of thin air but constructors are still called. I see [there is a GetUninitializedObject() call](http://stackoverflow.com/a/160027/316760) to create an object without calling constructors. I was hoping I could use the wrapping creation option. The `wrapped` object is created, but I get the same error when calling A.Fake<>(). If this is technically feasible, it would be neat to have a ".Uninitialized()" option in FIE. – Vimes Sep 11 '14 at 15:43
  • I'm not entirely sold on the uninitialized object option, even when used in conjunction with wrapping. One immediate problem is that DynamicProxy does not (currently) offer us such a solution. Leaving that aside, if constructors aren't fired, then the object is in an unknown and scary state, so _perhaps_ unusable. This may not be a big deal if we're just wrapping the object, but if that's the case, and we're going to override all the methods that are called, we might as well just be programming to an interface, and an easier way to do that is just to fake an interface… – Blair Conrad Sep 11 '14 at 16:56