Autofixture: Treat delegates as Fakes with FakeItEasy

Created on 16 Feb 2018  ·  15Comments  ·  Source: AutoFixture/AutoFixture

As far as I know, AutoFixture automatically treats interfaces and abstract types as mockables. I think it is a good idea to include the delegate types in this list as well.

enhancement good first issue

Most helpful comment

Released in v4.2.0! To enable:
c# fixture.Customize(new AutoFakeItEasyCustomization { GenerateDelegates = true });

All 15 comments

First of all, could you please clarify which mocking framework you work with? We have a few integrations (Moq, NSubsitute, FakeItEasy), so it's unclear. As far as I know, delegates are already supported by Moq integration (at least that is mentioned so in #742).

Also as far as I know AutoFixture has it's own delegates generation mechanism. Do you have any issues with it?

@zvirja I use FakeItEasy. So I am changing the title of the issue
The following code returns a Fake after the customization applied:
var myFake= fixture.Create<ISomeInterface>();

However the following doesn't:
var myFake= fixture.Create<Func<int,int>>();

Above code just returns a non fake dummy delegate.

Further more the following crashes with an error:

var myFake= fixture.Create<Fake<Func<int,int>>>();

AutoFixture.ObjectCreationExceptionWithPath
HResult=0x80131500
Message=AutoFixture was unable to create an instance from System.IntPtr because creation unexpectedly failed with exception. Please refer to the inner exception to investigate the root cause of the failure.

Request path:
FakeItEasy.Fake1[System.Func2[System.Int32,System.Int32]]
IntPtr method
System.IntPtr

Inner exception messages:
AutoFixture.Kernel.IllegalRequestException: A request for an IntPtr was detected. This is an unsafe resource that will crash the process if used, so the request is denied. A common source of IntPtr requests are requests for delegates such as Func or Action. If this is the case, the expected workaround is to Customize (Register or Inject) the offending type by specifying a proper creational strategy.

Source=
StackTrace:

Inner Exception 1:
IllegalRequestException: A request for an IntPtr was detected. This is an unsafe resource that will crash the process if used, so the request is denied. A common source of IntPtr requests are requests for delegates such as Func or Action. If this is the case, the expected workaround is to Customize (Register or Inject) the offending type by specifying a proper creational strategy.

@OnurGumus I've briefly investigated this issue and it doesn't seem that FakeItEasy supports delegate mocking. Delegates are not mentioned in their documentation.

Also I've tested the following code and it failed with an exception (which proves that feature is not supported):
c# A.Fake<Func<int, int>>();

Therefore, it doesn't seem that AutoFixture can do something about that as well.

Could you please describe the desired behavior in more detail? It's very easy to mock delegate, as it could be specified via a primitive lambda expression, so why do you need mocks?

@zvirja , FakeItEasy certainly supports Fakes for delegates:
image

@blairconrad Could you please find a few minutes and shed some light on this? Does FakeItEasy support mocking of the delegates? If so, what is the syntax? My sample above failed for me, but it could happen I missed something... :confused:

Thanks :wink:

@zvirja, happy to. As @OnurGumus says, FakeItEasy will happily fake delegates (and I've created an issue to document this).
However, AutoFakeItEasy has never bothered with faking delegates.

When faking a Fake<Func<int, int>>, for example, FakeItEasyMethodQuery.SelectMethods fails because it doesn't consider the case Fake<T> where T is a delegate. The very naive change below rectifies this.

@@ -33,7 +33,7 @@ namespace Ploeh.AutoFixture.AutoFakeItEasy
             }

             var fakeType = type.GetFakedType();
-            if (fakeType.IsInterface)
+            if (fakeType.IsInterface || fakeType.IsSubclassOf(typeof(Delegate)))
             {
                 return new[] { new ConstructorMethod(type.GetDefaultConstructor()) };
             }

Then I can create and use a Fake<Func<int, int>>.

As for creating a Func<int, int>, I gave it a quick go and found that the fixture used DelegateGenerator to create a (non-FakeItEasy) delegate.
Between you and me, I've always been confused by the AutoFixture architecture and the relationship between a builder and a relay and whatnot, but I can continue to explore and see where the gap is.

I'm back. I may understand. The FakeItEasyRelay is added to the fixture's residue collectors, but the DelegateGenerator creates a delegate first.
A quick search didn't reveal how to insert the FakeItEasy builder earlier in the chain, but I may not be looking right.

Regardless of how this pans out, I'd be happy to submit a PR to allow mocking of Fake<TDelegate> where TDelegate is a delegate type.

@blairconrad as a FakeItEasy user I long for your PR :)

@blairconrad Thanks for clarifications and for your investigation! I've just checked for one more time and found that FakeItEasy indeed create the fakes for delegates. Likely, I've missed something in my previous test 😖

As for creating a Func, I gave it a quick go and found that the fixture used DelegateGenerator to create a (non-FakeItEasy) delegate.

Yep. We have the exactly same issue with Moq - AutoFixture intercepts those requests before the ResidueCollectors have an opportunity to chime in.

I'd suggest to fix this issue in the following way:

  • Extend method query to support Fake<Delegate> requests;
  • Add a new customization, that will add relay to the Customizations collection, so delegate request is handled (relayed) _before_ being processed by the AutoFixture kernel. Meaning, that feature will not be activated by default, however users could easily enable it using a predefined customization.

@blairconrad Additionally, could you please describe what is the benefit of the faked delegate over the one created by the AutoFixture? For now only one thing comes to my mind - you can configure them using the fake library configuration syntax.

Thanks, @zvirja.

I think you're right. The benefit of a faked delegate would be that it could be configured using the fake library configuration syntax.

I'm happy to extend the method query to support Fake<Delegate>, and will add an additional customization just as you say, but wonder: is one that supports Delegate good enough? What happens when the next reported bug wishes IEnumerable or Task or IDictionary were faked by FakeItEasy?

Oh, @zvirja has asked for confirmation on the syntax for faking a delegate. It's now been merged into FakeItEasy's documentation with FakeItEasy/FakeItEasy#1321, but to be explicit:

```c#
var fakeDelegate = A.Fake>();

is the preferred syntax for clients, although "unnatural fakes" can be used:

```c#
var fakeDelegate = new Fake<Func<int, int>>();

The latter is how the FakeItEasyBuilder class would create the Fake.

Fun fact: faking delegates wasn't supported until version 1.7.4257.42 (probably commit FakeItEasy/FakeItEasy@edb4f61d0db0a84b68c7a9395f2661a58579d34a). Personally, I've no problem saying "you don't get this unless you use at least that version of FakeItEasy". @zvirja or others, do you consider this a problem? I'll have to do some trickery with the unit tests to make sure we don't break the existing AutoFakeItEasyUnitTest tests.
Unless you want to update the minimum FakeItEasy version for this fix. I probably wouldn't, especially since requesting a delegate would've failed on those versions anyhow.

@blairconrad Thanks a lot for your work on that!

@zvirja or others, do you consider this a problem?

No at all. According to the SemVer we are following here, we are not allowed to change the dependency version within the same major version. Therefore, I'd say that the right step would be to add such a support for the FakeItEasy versions that support the feature. Later we might want to drop 1.x support in v5 (for consistency), but that will happen in future only.

Converting this issue to a feature, as we agreed to proceed with it (and FakeItEasy supports delegates).

Released in v4.2.0! To enable:
c# fixture.Customize(new AutoFakeItEasyCustomization { GenerateDelegates = true });

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TroyHouston picture TroyHouston  ·  6Comments

zvirja picture zvirja  ·  8Comments

JoshKeegan picture JoshKeegan  ·  6Comments

josh-degraw picture josh-degraw  ·  4Comments

DeafLight picture DeafLight  ·  5Comments