Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interface method does not have an implementation exception #1257

Closed
skarum opened this issue Nov 7, 2017 · 13 comments
Closed

Interface method does not have an implementation exception #1257

skarum opened this issue Nov 7, 2017 · 13 comments

Comments

@skarum
Copy link
Collaborator

skarum commented Nov 7, 2017

When I upgraded a project today from 3.X to latests version 4.1.1 i got a odd error.

I have an interface like below and got the following error when calling A.Fake<Test>();

public interface Test
{
	void Test1<T>(IEnumerable<T> enumerable);
	void Test1<T>(IList<T> enumerable);
}

*ERROR

FakeCreationException: 
  Failed to create fake of type UserQuery+Test.

  Below is a list of reasons for failure per attempted constructor:
    No constructor arguments failed:
      No usable default constructor was found on the type UserQuery+Test.
      An exception of type System.TypeLoadException was caught during this call. Its message was:
      Method 'Test1' in type 'Castle.Proxies.ObjectProxy_9' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
         at System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type)
         at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
         at System.Reflection.Emit.TypeBuilder.CreateTypeInfo()
         at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.CreateType(TypeBuilder type) in C:\projects\fakeiteasy\src\FakeItEasy\AsyncReturnValueConfigurationExtensions.cs:line 0
         at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType() in C:\projects\fakeiteasy\src\FakeItEasy\AsyncReturnValueConfigurationExtensions.cs:line 0
         at Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateType(String name, Type[] interfaces, INamingScope namingScope) in C:\projects\fakeiteasy\src\FakeItEasy\AsyncReturnValueConfigurationExtensions.cs:line 0
         at Castle.DynamicProxy.Generators.ClassProxyGenerator.<>c__DisplayClass1_0.<GenerateCode>b__0(String n, INamingScope s) in C:\projects\fakeiteasy\src\FakeItEasy\AsyncReturnValueConfigurationExtensions.cs:line 0
         at Castle.DynamicProxy.Generators.BaseProxyGenerator.ObtainProxyType(CacheKey cacheKey, Func`3 factory) in C:\projects\fakeiteasy\src\FakeItEasy\AsyncReturnValueConfigurationExtensions.cs:line 0
         at Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateCode(Type[] interfaces, ProxyGenerationOptions options) in C:\projects\fakeiteasy\src\FakeItEasy\AsyncReturnValueConfigurationExtensions.cs:line 0
         at Castle.DynamicProxy.DefaultProxyBuilder.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options) in C:\projects\fakeiteasy\src\FakeItEasy\AsyncReturnValueConfigurationExtensions.cs:line 0
         at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors) in C:\projects\fakeiteasy\src\FakeItEasy\AsyncReturnValueConfigurationExtensions.cs:line 0
         at FakeItEasy.Creation.CastleDynamicProxy.CastleDynamicProxyGenerator.GenerateClassProxy(Type typeOfProxy, ProxyGenerationOptions options, IEnumerable`1 argumentsForConstructor, IInterceptor interceptor, IEnumerable`1 allInterfacesToImplement) in C:\projects\fakeiteasy\src\FakeItEasy\Creation\CastleDynamicProxy\CastleDynamicProxyGenerator.cs:line 174
         at FakeItEasy.Creation.CastleDynamicProxy.CastleDynamicProxyGenerator.DoGenerateProxy(Type typeOfProxy, ProxyGenerationOptions options, IEnumerable`1 additionalInterfacesToImplement, IEnumerable`1 argumentsForConstructor, IInterceptor interceptor) in C:\projects\fakeiteasy\src\FakeItEasy\Creation\CastleDynamicProxy\CastleDynamicProxyGenerator.cs:line 159
         at FakeItEasy.Creation.CastleDynamicProxy.CastleDynamicProxyGenerator.CreateProxyGeneratorResult(Type typeOfProxy, ProxyGenerationOptions options, IEnumerable`1 additionalInterfacesToImplement, IEnumerable`1 argumentsForConstructor, IFakeCallProcessorProvider fakeCallProcessorProvider) in C:\projects\fakeiteasy\src\FakeItEasy\Creation\CastleDynamicProxy\CastleDynamicProxyGenerator.cs:line 105

This works fine prior to 4.1.0 (both 3.X and 4.0.0).

I suspect it's the upgrade of Castle.Core 4.1 to 4.2, but I'm unsure, because if I add a reference to Castle.Core 4.2.1 in my project it still work with FakeItEasy 4.0.0 and earlier.

@blairconrad
Copy link
Member

@skarum, thanks for raising this.

A couple of quick questions.

  1. I see that Test is a nested interface. Is UserQuery public? Could we see the declaration?
  2. Are you using the .NET Framework version of FakeItEasy (for .NET 4.0 or 4.5)? It ILMerges Castle.Core, so referencing Castle.Core 4.2.1 would have no effect on it (but it would make a difference if you were using the .NET Standard version of the library)

@blairconrad
Copy link
Member

I've reproduced using the .NET Framework flavours of FakeItEasy, both under 4.1 and 4.1.1:

public class UserQuery
{
    public interface Test
    {
        void Test1<T>(IEnumerable<T> enumerable);
        void Test1<T>(IList<T> enumerable);
    }

    [NUnit.Framework.Test]
    public void MakeAFake()
    {
        var fake = A.Fake<Test>();
    }
}

@thomaslevesque
Copy link
Member

thomaslevesque commented Nov 7, 2017

I see that Test is a nested interface. Is UserQuery public? Could we see the declaration?

It's probably in a LINQPad query.

I suspected it might be related to the fact the IList<T> inherits IEnumerable<T>, but I can reproduce with dummy interfaces like this:

public interface IFoo
{
    int Bar<T>(I1<T> x);
    int Bar<T>(I2<T> x);
}

public interface I1<T> { }
public interface I2<T> { }

The problem occurs only if both interfaces are generic.

My guess is that it's a bug in Castle.Core, since FakeItEasy doesn't really go into the details of creating the proxy type. @jonorossi have you ever seen this?

@blairconrad
Copy link
Member

My guess is that it's related to castleproject/Core#310. I haven't examined the details of the changes, but it has all the right buzzwords.

@skarum
Copy link
Collaborator Author

skarum commented Nov 7, 2017

@blairconrad

  1. UserQuery is just the from LinqPad, this is all my code:
void Main()
{
    A.Fake<Test>();
}

public interface Test
{
	void Test1<T>(IEnumerable<T> enumerable);
	void Test1<T>(IList<T> enumerable);
}

LinqPad is running 4.6 framework.

It makes sense if you IL merge that is a Castle bug

@thomaslevesque
Copy link
Member

My guess is that it's related to castleproject/Core#310.

Indeed. I can reproduce the bug with Castle.Core 4.2.0, but not with 4.2.1. So upgrading to 4.2.1 should fix the problem in FakeItEasy as well.

@blairconrad
Copy link
Member

I can work on the fix, unless someone on the thread prefers to.
@skarum, you found it. Do you want the glory?

@skarum
Copy link
Collaborator Author

skarum commented Nov 7, 2017

I can do that :) I'll look at it tonight (Europe time) and make a PR. If it''s faster for you to make it yourself, then just go ahead.

@blairconrad
Copy link
Member

blairconrad commented Nov 7, 2017

Oh, it's nearly always faster for us to do it, but we very much enjoy community contributions (and assume that the community members enjoy making them - if I'm wrong here, please correct me).

If nothing else, I'm in mid-East Canada time, so you can always report back tonight with your progress and/or feelings, and I'll be awake. ;-)

I will warn you, though, that I'd enjoy a new spec for the use case. I would add a new Scenario to CreationSpecsBase. If you've not used xBehave.net before, fret not, since it's a joy to use and the examples in that file are good to copy. In particular, CollectionOfFakeWithOptionBuilder is a good starting point (except that you'd only make one fake, of course).

The only tricky part about the test would be that since CreationSpecsBase is an abstract base class designed to test both generic and non-generic test creation methods. I'd create your fake using this.CreateFake<>() instead of the standard A.Fake<>().

Again, thanks for raising the issue and for agreeing to help us fix it!

@blairconrad blairconrad assigned skarum and unassigned blairconrad Nov 7, 2017
@blairconrad blairconrad added this to the vNext milestone Nov 7, 2017
@blairconrad blairconrad added the P1 label Nov 7, 2017
@thomaslevesque
Copy link
Member

thomaslevesque commented Nov 7, 2017

@blairconrad, this issue makes me wonder if we should really ILMerge Castle.Core... We probably discussed it before, but there's a compelling argument against it: in such a case when there's a bug in Castle.Core, the end-user can't just work around the issue by upgrading to the next version of Castle.Core where the issue is fixed. They have to wait for us to release a version that uses the newer Castle.Core.

@blairconrad
Copy link
Member

@thomaslevesque, I see your point. I think the original reason for merging was to work around situations where users' production code required a different version of Castle.Core than we liked. What's the lesser of the two evils?

@thomaslevesque
Copy link
Member

What's the lesser of the two evils?

I'm not sure... if they want a more recent version, fine, but if they need an older one, they're in trouble. Maybe we should offer a separate package where Castle.Core is not ILMerged. In fact, if we do that, it would be more consistent to stop using ILMerge in the main package, and do it only in the new one (which would only target net40/net45, since ILMerge doesn't work with .NET Standard). Anyway, it's just a random idea, and a bit off-topic...

This was referenced Nov 7, 2017
blairconrad added a commit that referenced this issue Nov 7, 2017
Updated Castle.Core from 4.2.0 to 4.2.1 to fix issue described in #1257.
@blairconrad blairconrad changed the title Interface does not have an implementation exception Interface method does not have an implementation exception Nov 7, 2017
@blairconrad
Copy link
Member

blairconrad commented Nov 8, 2017

This change has been released as part of FakeItEasy 4.2.0.

Thanks for your help with this, @skarum. Look for your name in the release notes! 🏆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants