moq icon indicating copy to clipboard operation
moq copied to clipboard

Constructing Mock<DerivedControl>.Object throws TypeLoadException

Open andrewimcclement opened this issue 1 year ago • 3 comments

Describe the Bug

Given an internal class DerivedControl inheriting from System.Windows.Forms.Control, constructing new Mock<DerivedControl>().Object throws ArgumentException, wrapping TypeLoadException. I cannot reproduce this issue without using System.WIndows.Forms.Control.

I am not certain if this is a bug in Castle.Core, but thought I would ask here first.

Steps to Reproduce

Full code with additional examples can be found at https://github.com/andrewimcclement/MoqInternalsVisibleToIssue.

This test (in Production.Tests.csproj) passes on .NET Framework 4.8 but fails on .NET 6.0 & .NET 8.0.

using NUnit.Framework;
using Moq;

namespace Production.Tests;

public class DerivedControl : Control {}

internal class InternalsVisibleToTests
{
    [Test]
    public void FailsOnNetCore()
    {
        var obj = new Mock<DerivedControl>();
        Assert.That(() => obj.Object, Throws.Nothing);
    }
}

Expected Behavior

Given this passes on .NET Framework 4.8, I would expect this to pass (or understand why System.Windows.Forms.Control is special

Exception with Stack Trace

.NET 8.0 stack trace: (.NET 6.0 stack trace is similar)

System.ArgumentException : Type to mock (Production.InternalDerivedControl) must be an interface, a delegate, or a non-sealed, non-static class.
  ----> System.TypeLoadException : Method 'NotifyValidationResult' on type 'Castle.Proxies.InternalDerivedControlProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is overriding a method that is not visible from that assembly.
   at Moq.CastleProxyFactory.CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, Object[] arguments) in /_/src/Moq/Interception/CastleProxyFactory.cs:line 114
   at Moq.Mock`1.InitializeInstance() in /_/src/Moq/Mock`1.cs:line 502
   at Moq.Mock`1.OnGetObject() in /_/src/Moq/Mock`1.cs:line 516
   at Moq.Mock.get_Object() in /_/src/Moq/Mock.cs:line 180
   at Moq.Mock`1.get_Object() in /_/src/Moq/Mock`1.cs:line 453
   at Production.Tests.InternalsVisibleToTests.FailsOnNetCore() in C:\Git\github\andrewimcclement\MoqInternalsVisibleToIssue\Production.Tests\UnitTest1.cs:line 21
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
--TypeLoadException
   at System.Reflection.Emit.RuntimeTypeBuilder.CreateTypeNoLock()
   at System.Reflection.Emit.RuntimeTypeBuilder.CreateTypeInfoImpl()
   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.BaseClassProxyGenerator.GenerateType(String name, INamingScope namingScope)
   at Castle.DynamicProxy.Generators.BaseProxyGenerator.<>c__DisplayClass13_0.<GetProxyType>b__0(CacheKey cacheKey)
   at Castle.Core.Internal.SynchronizedDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Castle.DynamicProxy.Generators.BaseProxyGenerator.GetProxyType()
   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) in /_/src/Moq/Interception/CastleProxyFactory.cs:line 110

Note that InternalDerivedControl is a non-sealed, non-static class, so even if this behaviour is correct, the error message seems inaccurate. Also, looking at the source code of System.Windows.Forms.Control, I cannot see any relevant change to internal virtual void NotifyValidationResult

Version Info

Moq 4.20.70 used (implicitly using Castle.Core 5.1.1).

Additional information

I hit this issue while trying to upgrade some test projects from .NET Framework to .NET 8.

Back this issue Back this issue

andrewimcclement avatar Jul 04 '24 23:07 andrewimcclement

Have you set the <UseWindowsForms>true</UseWindowsForms> when using .NET6/8 as documented?

kzu avatar Jul 24 '24 01:07 kzu

Yes - see https://github.com/andrewimcclement/MoqInternalsVisibleToIssue/blob/cd273f348e2edf7ab301dfa452fece060c85bff8/Production/Production.csproj#L8 and https://github.com/andrewimcclement/MoqInternalsVisibleToIssue/blob/cd273f348e2edf7ab301dfa452fece060c85bff8/Production.Tests/Production.Tests.csproj#L8.

andrewimcclement avatar Jul 24 '24 08:07 andrewimcclement

Due to lack of recent activity, this issue has been labeled as 'stale'. It will be closed if no further activity occurs within 30 more days. Any new comment will remove the label.

github-actions[bot] avatar May 08 '25 01:05 github-actions[bot]

This issue will now be closed since it has been labeled 'stale' without activity for 30 days.

github-actions[bot] avatar Jun 07 '25 01:06 github-actions[bot]