Skip to content

Constructing Mock<DerivedControl>.Object throws TypeLoadException #1488

Open
@andrewimcclement

Description

@andrewimcclement

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions