devlooped/moq · error · ArgumentException

Type to mock ( ) must be an interface, a delegate, or a…

Error message

Type to mock ({0}) must be an interface, a delegate, or a non-sealed, non-static class.

What it means

Moq creates mock objects by generating a dynamic proxy via Castle DynamicProxy in CastleProxyFactory.CreateProxy. When the proxy generator fails with a TypeLoadException, Moq rethrows it as an ArgumentException using the TypeNotMockable resource: the target type must be an interface, a delegate, or a non-sealed, non-static class. Sealed classes, static classes, and types with inaccessible internals cannot be proxied.

Solutions

  1. Mock the interface the class implements instead of the sealed class itself.
  2. Remove 'sealed' from the class (and make mocked members public virtual) if you own the code.
  3. For internal types, add [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] to the mocked assembly.
  4. Wrap the unmockable dependency behind your own interface for testability.

Example fix

// before
var mock = new Mock<string>(); // string is sealed
// after
var mock = new Mock<IMessageBuilder>(); // mock the interface instead
Defensive patterns

Strategy: validation

Validate before calling

static bool IsMockable(Type t) =>
    t.IsInterface || t.IsSubclassOf(typeof(Delegate)) ||
    (!t.IsSealed && !t.IsStatic() && !t.IsAbstractWithNoCtorAccessible());
// simplest safe check:
// if (type.IsSealed || type.IsAbstract) throw new InvalidOperationException($"{type.Name} cannot be mocked; mock an interface instead.");

Type guard

static bool CanMock(Type t) => t.IsInterface || (!t.IsSealed && !t.IsAbstract || t.IsSubclassOf(typeof(Delegate)));

Try / catch

try { var mock = new Mock<T>(); }
catch (ArgumentException ex) when (ex.InnerException is TypeLoadException) { /* fall back to interface mock or manual fake */ }

Prevention

When it happens

Trigger: Calling Mock.Of<T>() or new Mock<T>() where T is a sealed class (e.g. string or a sealed DTO), a static class, or a non-public/inaccessible class whose methods Castle cannot override; CreateProxy catches TypeLoadException and throws this ArgumentException.

Common situations: Attempting to mock types from the BCL like string, HttpClient (partially), or framework sealed types; mocking third-party classes that were made sealed in a newer library version; trying to mock internal classes without InternalsVisibleTo("DynamicProxyGenAssembly2").

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16). Data as JSON: /api/errors/d6e335f35c8c9151. Report an issue: GitHub.

Appendix: source

Thrown at src/Moq/Interception/CastleProxyFactory.cs:74

                // While `CreateClassProxy` could also be used for interface types,
                // `CreateInterfaceProxyWithoutTarget` is much faster (about twice as fast):
                return generator.CreateInterfaceProxyWithoutTarget(mockType, additionalInterfaces, this.generationOptions, new Interceptor(interceptor));
            }
            else if (mockType.IsDelegateType())
            {
                var options = new ProxyGenerationOptions();
                options.AddDelegateTypeMixin(mockType);
                var container = GetClassGenerator(mockType).CreateClassProxy(typeof(object), additionalInterfaces, options, new Interceptor(interceptor));
                return Delegate.CreateDelegate(mockType, container, container.GetType().GetMethod("Invoke")!);
            }

            try
            {
                return GetClassGenerator(mockType).CreateClassProxy(mockType, additionalInterfaces, this.generationOptions, arguments, new Interceptor(interceptor));
            }
            catch (TypeLoadException e)
            {
                throw new ArgumentException(string.Format(Resources.TypeNotMockable, mockType), e);
            }
            catch (MissingMethodException e)
            {
                throw new ArgumentException(Resources.ConstructorNotFound, e);
            }
        }

        public override bool IsMethodVisible(MethodInfo method, out string messageIfNotVisible)
        {
            return ProxyUtil.IsAccessible(method, out messageIfNotVisible);
        }

        public override bool IsTypeVisible(Type type)
        {
            return ProxyUtil.IsAccessible(type);
        }

        sealed class Interceptor : Castle.DynamicProxy.IInterceptor

View on GitHub (pinned to 89a5be629c)