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

Cannot mock subclass of superclass with internal virtual methods #991

Open
GeeWee opened this issue Apr 3, 2020 · 8 comments
Open

Cannot mock subclass of superclass with internal virtual methods #991

GeeWee opened this issue Apr 3, 2020 · 8 comments

Comments

@GeeWee
Copy link

GeeWee commented Apr 3, 2020

Unfortunately, this bug report is a little confusing, as I have tried multiple times to create a small reproducible example, but I am unable to.

I'm trying to make a mock of the Azure BlockBlobClient
This works fine, I can easily execute

var clientMock = new Mock<BlockBlobClient>();
var obj = clientMock.Object;

However, if I subclass the BlockBlobClient, everything goes wrong, even if the subclass does nothing.

[Fact]
public void ShouldBeAbleToSubclass()
{
    // Arrange
    var clientMock = new Mock<TestBlockBlobClient>();
    var obj = clientMock.Object;
}

}

public class TestBlockBlobClient : BlockBlobClient
{
}

I then get the following error:

  Error Message:
   System.ArgumentException : Type to mock must be an interface, a delegate, or a non-sealed, non-static class.
---- System.TypeLoadException : Method 'get_Pipeline' on type 'Castle.Proxies.TestBlockBlobClientProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is overriding a method that is not visible from that assembly.
  Stack Trace:
     at Moq.CastleProxyFactory.CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, Object[] arguments)
   at Moq.Mock`1.InitializeInstance()
   at Moq.Mock`1.OnGetObject()
   at Moq.Mock.get_Object()
   at Moq.Mock`1.get_Object()
   at AIP.Shared.Test.BlockBlobClientTests.ShouldBeAbleToSubclass() in /home/geewee/programming/OAI/Shared.Test/BlockBlobClientTests.cs:line 91
----- Inner Stack Trace -----
   at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
   at System.Reflection.Emit.TypeBuilder.CreateTypeInfo()
   at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.CreateType(TypeBuilder type)
   at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType()
   at Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateType(String name, Type[] interfaces, INamingScope namingScope)
   at Castle.DynamicProxy.Generators.ClassProxyGenerator.<>c__DisplayClass1_0.<GenerateCode>b__0(String n, INamingScope s)
   at Castle.DynamicProxy.Generators.BaseProxyGenerator.<>c__DisplayClass33_0.<ObtainProxyType>b__0(CacheKey _)
   at Castle.Core.Internal.SynchronizedDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Castle.DynamicProxy.Generators.BaseProxyGenerator.ObtainProxyType(CacheKey cacheKey, Func`3 factory)
   at Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateCode(Type[] interfaces, ProxyGenerationOptions options)
   at Castle.DynamicProxy.DefaultProxyBuilder.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
   at Moq.CastleProxyFactory.CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, Object[] arguments)

It seems like Moq is trying to mock the internal virtual method Pipeline in the BlobBaseClient, which the BlockBlobClient inherits from.

Seeing as the method is internal and in another assembly, I cannot override it. However if I define a property with the same name, ala:

public class TestBlockBlobClient : BlockBlobClient
{
    public HttpPipeline Pipeline => null!;
}

I get the same error, but with the next internal virtual property

 System.TypeLoadException : Method 'get_Version' on type 'Castle.Proxies.TestBlockBlobClientProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is overriding a method that is not visible from that assembly.

which is also a property on the superclass

So my theory is that there's something that's going on with the internal virtual properties, but only in a subclass of a subclass in another assembly.

Unfortunately, I cannot reproduce the problem, if I try to mimick the same thing locally (e.g. create two assemblies and the same internal virtual structure). I'm not sure if this is a Windsor or a Moq problem either.

@stakx
Copy link
Contributor

stakx commented Apr 3, 2020

Interesting.

I can't tell you right away why that happens, but I can tell you that the problem happens in the infrastructure below Moq, not in Moq itself. I've opened a bug report over at the DynamicProxy repository (castleproject/Core#488). I'll close your issue here, since there's nothing we can do in Moq.

P.S.: Some more info in the Castle.Core issue that might be useful to you.

@stakx
Copy link
Contributor

stakx commented Apr 3, 2020

I closed this too early... sorry. Let's close this once we've updated to a new version of DynamicProxy that contains a fix for this.

@stakx stakx reopened this Apr 3, 2020
@GeeWee
Copy link
Author

GeeWee commented Apr 6, 2020

Thanks for the quick response! I see now why I couldn't reproduce it locally, as it was a little more complicated than I gave it credit for.

@smokedlinq
Copy link

Ran across this today, did anyone find a way around this or did they have to redesign the clients that are inheriting from those types of base classes?

@thackman7
Copy link

I'm running in to the same problem trying to create a mock for a named subclass of Azure DataLakeServiceClient.

Inner Exception 1:
TypeLoadException: Method 'get_ClientConfiguration' on type 'Castle.Proxies.MyAppClientProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is overriding a method that is not visible from that assembly.

@Identekit
Copy link

Identekit commented May 2, 2023

I just ran into this same issue with one of my own classes.

System.TypeLoadException: Method 'MyMethod' on type 'Castle.Proxies.MyClassProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is overriding a method that is not visible from that assembly.

This is in a .NET 6 project...I might not have made internals visible in the correct way. There's no Assembly.cs file anymore and I couldn't find much info on how to do this in .NET 6 except for the following (which may not be correct)

MyProject.csproj

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
    <_Parameter1>MyProject.Test</_Parameter1>		
  </AssemblyAttribute>
</ItemGroup>

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
    <_Parameter1>InternalsVisible.DynamicProxyGenAssembly2</_Parameter1>		
  </AssemblyAttribute>
</ItemGroup>

UPDATE
Turns out the problem I was having was in "InternalsVisible.DynamicProxyGenAssembly2". Once I removed the "InternalsVisible." part everything worked.

MyProject.csproj

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
    <_Parameter1>MyProject.Test</_Parameter1>		
  </AssemblyAttribute>
</ItemGroup>

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
    <_Parameter1>DynamicProxyGenAssembly2</_Parameter1>		
  </AssemblyAttribute>
</ItemGroup>

I also found that as of .NET 5 and higher, the following also works (source: here):

MyProject.csproj

  <ItemGroup>
	<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
  </ItemGroup>

  <ItemGroup>
    <InternalsVisibleTo Include="MyProject.Test" />
  </ItemGroup>

@gorillapower
Copy link

I have had some serious code rage with this issue. Any workarounds for when you need to create a subclass like OP has? My issue is when I try create a subclass of BlobContainerClient

@mrobinson-campion
Copy link

I'm getting the same issue when I try create a subclass of BlobServiceClient

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

7 participants